'React 'cannot read property of undefined' when using map

I'm making a very basic React app from teamtreehouse.com, and I'm constantly encountering

"TypeError: Cannot read property 'onPlayerScoreChange' of undefined"

even though I'm binding my functions correctly (I think)

'onPlayerScoreChange' Is a method in the Grandparent component which executes when a user hits a '+' or '-' button to change a player's score.

It would be really helpful if someone could explain what is wrong, because I think I am setting this.onPlayerScoreChange = this.onPlayerScoreChange.bind(this) in the great grandparent's constructor.

Parent component:

class App extends React.Component {
constructor(props) {
    super(props);
    this.onPlayerScoreChange = this.onPlayerScoreChange.bind(this)
    this.state = {
        initialPlayers: props.initialPlayers,
    };
}

onPlayerScoreChange(delta, index) {
    this.setState((prevState, props) => {
        return {initialPlayers: this.prevState.initialPlayers[index].score += delta}
    })
}

render() {
    return(
        <div className = "scoreboard">
            <Header title = {this.props.title}/>
            <div className = "players">
                {this.state.initialPlayers.map(function(player, index) {
                    return(
                        <Player 
                        name = {player.name} 
                        score = {player.score} 
                        key = {player.id} 
                        index = {index}
                        onScoreChange = {this.onPlayerScoreChange}
                        />
                    )
                })}
            </div>
        </div>
    )
}}

(Component has default props for title)

Child component:

class Player extends React.Component {
render() {
    return(
        <div className = "player">
            <div className = "player-name">
                {this.props.name}
            </div>
            <div className = "player-score">
                <Counter score = {this.props.score} onChange = {this.props.onScoreChange} index = {this.props.index}/>
            </div>
        </div>
)
}}

Grandchild component:

class Counter extends React.Component {
constructor(props) {
    super(props)
    this.handleDecrement = this.handleDecrement.bind(this)
    this.handleIncrement = this.handleIncrement.bind(this)
}

handleDecrement() {
    this.props.onChange(-1, this.props.index)
}

handleIncrement() {
    this.props.onChange(1, this.props.index)
}

render() {
    return(
        <div className = "counter">
            <button className = "counter-action decrement" onClick = {this.handleDecrement}> - </button>
            <div className = "counter-score"> {this.props.score} </div>
            <button className = "counter-action increment" onClick = {this.handleIncrement}> + </button>
        </div>
)}}

Thank you!



Solution 1:[1]

Can also be done by passing second argument as this to map function as onClick event uses local this of map function which is undefined here and this currently refers to the global object.

{this.state.initialPlayers.map(function(player, index) {
                    return(
                        <Player 
                        name = {player.name} 
                        score = {player.score} 
                        key = {player.id} 
                        index = {index}
                        onScoreChange = {this.onPlayerScoreChange}
                        />
                    )
                }),this}

Solution 2:[2]

sometimes it is quite easy and it all depends on how you declare your loop

example you will get this error if you try to do something like this var.map(function(example, index) {}

but if you call the new function within the map with this

this.sate.articles.map(list => 
{<a onClick={() => this.myNewfunction()}>{list.title}</a>)}

the second loop will get you out of the undefined error

and dont forget to bind your new function

Solution 3:[3]

//This should be declared befor map function

const thObj = this;

this.sate.articles.map(list => {<a onClick={() => thObj.myNewfunction()}>{list.title})}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Vikram Saini
Solution 2 jerryurenaa
Solution 3 Mahesan Rv