Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

A R
A R
12,834 Points

Running into issue with 'this' and react

I'm not sure exactly what's up, it seems like there's an issue passing the props between the AddPlayerForm and the App, but I'm not sure what I'm doing wrong. I get errors like this:

 TypeError: this.state is undefined
handleAddPlayer
src/components/App.js:66

  63 | 
  64 | handleAddPlayer(name){
  65 |   this.setState({
> 66 |       players:[
  67 |           //brings in old array of players and tacks on news ones.
  68 |           ...this.state.players,
  69 |       {   name,

AddPlayerForm/_this.handleSubmit
src/components/AddPlayerForm.js:17

  14 |     //prevents form from submitting
  15 |     e.preventDefault();
  16 |     //adds player using the form's value as information
> 17 |     this.props.addPlayer(this.state.value);
  18 |     this.setState({ value: '' });
  19 | }
  20 | 

for reference, here is my code. APP.JS

//named import lets you create Class without extending React.Component
//Class app extends Component, not React.Component
import React, { Component } from 'react';
import Header from './Header';
import Player from './Player';
import AddPlayerForm from './AddPlayerForm';

class App extends Component {
  state = {
    players: [
      {
        name: "Guil",
        score: 0,
        id: 1
      },
      {
        name: "Treasure",
        score: 0,
        id: 2
      },
      {
        name: "Ashley",
        score: 0,
        id: 3
      },
      {
        name: "James",
        score: 0,
        id: 4
      }
    ]
  };

   //player id counter
   prevPlayerId = 4;

  handleScoreChange = (index, delta) => {
    this.setState( prevState => {
      // New 'players' array – a copy of the previous `players` state
      const updatedPlayers = [ ...prevState.players ];
      // A copy of the player object we're targeting
      const updatedPlayer = { ...updatedPlayers[index] };

      // Update the target player's score
      updatedPlayer.score += delta;
      // Update the 'players' array with the target player's latest score
      updatedPlayers[index] = updatedPlayer;

      // Update the `players` state without mutating the original state
      return {
        players: updatedPlayers
      };
    });
  }

  handleRemovePlayer = (id) => {
    this.setState( prevState => {
      return {
        players: prevState.players.filter(p => p.id !== id)
      };
    });
  }

  handleAddPlayer(name){
    this.setState({
        players:[
            //brings in old array of players and tacks on news ones.
            ...this.state.players,
        {   name,
            score: 0,
            //increments id by one each time
            id: this.prevPlayerId +=1,
        }
        ]
    })
}

  render() {
    return (
      <div className="scoreboard">
        <Header 
          title="Scoreboard" 
          players={this.state.players} 
        />

        {/* Players list */}
        {this.state.players.map( (player, index) =>
          <Player 
            name={player.name}
            score={player.score}
            id={player.id}
            index={index}
            key={player.id.toString()} 
            changeScore={this.handleScoreChange}
            removePlayer={this.handleRemovePlayer}           
          />
        )}
        <AddPlayerForm addPlayer={this.handleAddPlayer}/>
      </div>
    );
  }
}

export default App;

and AddPlayerForm

import React, { Component } from 'react';

class AddPlayerForm extends Component {
    state = {
        value:''
    };

    //adds the form value to this components' state
    handleValueChange = (e) => {
        this.setState({ value: e.target.value });
      }

    handleSubmit = (e)=>{
        //prevents form from submitting
        e.preventDefault();
        //adds player using the form's value as information
        this.props.addPlayer(this.state.value);
        this.setState({ value: '' });
    }

    render(){
        return(
            //uses React's onSubmit property with a function the user writes
            <form onSubmit={this.handleSubmit}>
                <input
                  type="text"
                  value={this.state.value}
                  onChange={this.handleValueChange}
                  placeholder="Enter a player's name"
                />
                <input
                  type="submit"
                  value="Add Player"
                />
            </form>
        )
    }
}

export default AddPlayerForm;

3 Answers

Steven Parker
Steven Parker
231,153 Points

I see you have arrow functions in this code, and one of the ways they are different from conventional functions is that they don't establish a "this" value.

For more details about it, and about the other differences, see the MDN page on Arrow function expressions.

A R
A R
12,834 Points

Thanks, I'll give it a try!

A R
A R
12,834 Points

I changed the handleAddPlayer to an arrow function and the rest to normal functions. I guess the 'this' in the handleAddPlayer was getting passed on instead of being used to establish a players array and that was causing the problem...

  handleAddPlayer = (name) => {
    this.setState({
      players: [
        ...this.state.players,
        {
          name,
          score: 0,
          id: this.prevPlayerId += 1
        }
      ]
    })
  }