JavaScript React Basics (retired) Designing Data Flow Building the Statistics Component

totalPoints doesn't update when the user increases or decreases the counter for a player

Hi,

I am running this locally using React App, so slightly different to the way the video does it, but everything has worked so far for me, a be it with a little googling and figuring out for myself.

However i am stuck with this, my code looks the same as on screen and in the download files, but the totalPoints doesn't update.

totalPoints = this.props.players.reduce(function (total, player) {

        return total + player.score;

    }, 0);

The totalPoints correctly displays, but doesn't get updated when the user changes the score using the + or - buttons

Update:

This is also the same for adding and removing users, in the next video. The users are added and removed fine, but the playters stat is not updated.

5 Answers

Mike Jensen
Mike Jensen
11,718 Points

Can you post your code for how you update the state when a player's individual score changes? If the total points shown is initially correct, and a player's individual score also seems to update in response to the +/- buttons being clicked, you may not be updating your state correctly.

Here is my example (also using a local create-react-app, with components in separate files):

  onScoreChange(index, delta) {
    const player = [...this.state.players][index]

    player.score += delta
    this.setState(player)
  }

Hi There,

Thanks for responding.

My code is this:

onScoreChange = (index, delta) => {

        this.setState(prevState => {

            return {

                score: prevState.players[index].score += delta

            };

        });

    };

I have tried you code snippet and this didn't work for me either. But my understanding is lacking it seems, having now looked back at this, so think i will run through this bit of the course again anyway.

Mike Jensen
Mike Jensen
11,718 Points

Where are you defining onScoreChange?

This should be defined in the nearest common ancestor of the components which are passed player information.

onScoreChange should then be passed in to a property as a callback from the ancestor component, all the way through the the counter component where the click event happens.

In my app this looks a little like this:

//App.js
...

onScoreChange() {
... code here
}

render() {
...
<Player
 onScoreChange={(delta) => this.onScoreChange(index, delta)}
 ...
/>
}

//Player.js
...

render() {
 ...

 <Counter
  score={this.props.score}
 onChange={this.props.onScoreChange}
/>
}

// Counter.js
...

render() {
...

<button className="counter-action decrement" onClick={() => this.props.onChange(-1)}> - </button>
}

Here is the reference article which describes how state should be lifted up to the nearest common ancestor, and the use of callbacks to pass requests for mutation back up through the component cascade:

https://reactjs.org/docs/lifting-state-up.html

Hi Again,

I have managed to fix this issue, but to be honest i don't know why it works and the original way didn't. I suspect its something to do with 'this' and scope, but not sure.

The fix was to take the values from TotalPlayers and TotalPoints and put them directly into the <td>, rather than calling them in the <td>

Original:

<tbody>

                    <tr>

                        <td>Players:</td>
                        <td>{this.totalPlayers}</td>

                    </tr>

                    <tr>

                        <td>Total Points:</td>
                        <td>{this.totalPoints}</td>

                    </tr>

                </tbody>

Fix:

<tbody>

                    <tr>

                        <td>Players:</td>
                        <td>{this.props.players.length}</td>

                    </tr>

                    <tr>

                        <td>Total Points:</td>
                        <td>{this.props.players.reduce(function (total, player) {

                            return total + player.score;

                        }, 0)}</td>

                    </tr>

                </tbody>
Mike Jensen
Mike Jensen
11,718 Points

Hi Duncan,

Could you post your full original code for your Stats component?

Here is mine for example:

import React, { Component } from 'react'
import PropTypes from 'prop-types'

export default class Stats extends Component {
  render() {
    const totalPlayers = this.props.players.length
    const totalPoints = this.props.players.reduce((accumulator, player) => accumulator + player.score, 0)

    return (
      <table className="stats">
        <thead></thead>
        <tbody>
          <tr>
              <td>Players:</td>
              <td>{totalPlayers}</td>
          </tr>
          <tr>
            <td>Total points:</td>
            <td>{totalPoints}</td>
          </tr>
        </tbody>
      </table>
    )
  }
}

Stats.propTypes = {
  players: PropTypes.array.isRequired
}

Hi Mike,

I have worked out where i have gone wrong from your code example. I had my totalPlayers and totalPoints variables in the wrong place in my code.

This all now works fine.

Thank you very much for all your help.