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 React Components Data Flow Update State Based on a Player's Id

jobbol
seal-mask
.a{fill-rule:evenodd;}techdegree
jobbol
Full Stack JavaScript Techdegree Student 16,610 Points

Set state function updates twice

I noticed handleScoreChange could be simplified by changing the code from:

const [players, setPlayers] = React.useState(/**/);

const handleScoreChange = (id, delta) => {
  setPlayers(prevPlayers => prevPlayers.map(player => { 
    if (player.id === id) { //changing this if statement
      return {
        name: player.name,
        score: player.score + delta,
        id: player.id
      }
    }
    return player;
  }));
}

To:

    if (player.id === id) { //To this 
        player.score += delta; 
    }
    return player;
  }));
}

However, React runs this twice. Clicking the increment button causes the score to be two instead of one.

I tested this outside of React with vanilla JS here and saw no issue: https://jsbin.com/tocaxufipe/edit?js,console

Why does React do this differently?

1 Answer

Travis Alstrand
seal-mask
.a{fill-rule:evenodd;}techdegree seal-36
Travis Alstrand
Data Analysis Techdegree Graduate 45,998 Points

The behavior you're observing is related to the immutability of state in React. React uses a shallow comparison when determining whether to re-render components or not. When you mutate the state directly (as in your simplified if statement), React might not be able to correctly detect changes and could lead to unexpected behavior.

In your original code, you're creating a new player object when you need to update the score. This guarantees a new reference in memory, allowing React to easily detect changes using a shallow comparison.

return {
  name: player.name,
  score: player.score + delta,
  id: player.id
}

In the modified version, you're directly modifying the score property on the existing player object:

player.score += delta;

This means the reference to the player object hasn't changed, so React may not realize that a change has been made. As a result, it can lead to unexpected re-renders or missed updates.

The original approach adheres to the best practice of not mutating state directly in React. Always ensure you return a new object or array when updating the state, as this ensures React can correctly and efficiently detect changes and re-render components as needed.

jobbol
seal-mask
.a{fill-rule:evenodd;}techdegree
jobbol
Full Stack JavaScript Techdegree Student 16,610 Points

That makes so much sense with the new Object creating a new memory reference, because I had also tried:

return {...player, score: player.score + delta};

And this ended up working.
Thanks again. I will remember to create new objects and arrays.