Pocketninja
Pocketninja

Reputation: 415

REACT ES6 Update state from parents updated props

I'm just following a Treehouse Tutorial in an older verison of React which I'm trying to follow with ES6.

I have a Stats component which displays the number of players and the total score. You can add a player and alter the scores while all the time the props are fed through the chains.

You can clearly see in the Developer Tools react tab when selecting the Stats Dom element that props is updated when added a new player. So the props are fine.

Heres the code:

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

class Stats extends Component {
  constructor(props) {
    super(props);

    this.state = {
      totalPlayers: props.players.length,
      totalPoints: props.players.reduce((total, player) => {
        return total + player.score;
      }, 0)
    };
  }

  render() {
    return (
      <table className="stats">
        <tbody>
          <tr>
            <td>Players:</td>
            <td>{this.state.totalPlayers}</td>
          </tr>
          <tr>
            <td>Total Points:</td>
            <td>{this.state.totalPoints}</td>
          </tr>
        </tbody>
      </table>
    );
  }
}

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

export default Stats;

Basically the totalPlayers and totalPoints shows on load but when making any further edits nothing updates. I know within the render template I could directly add this.props.players.length and also the props.players.reduce(...) and that works, but I think this props to state updating from the parent question is a blocker for me until it's answered.

I know it works like this

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

class Stats extends Component {  
  render() {
    return (
      <table className="stats">
        <tbody>
          <tr>
            <td>Players:</td>
            <td>{this.props.players.length}</td>
          </tr>
          <tr>
            <td>Total Points:</td>
            <td>{this.props.players.reduce((total, player) => {
                  return total + player.score;
                }, 0)}
            </td>
          </tr>
        </tbody>
      </table>
    );
  }
}

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

export default Stats;

But I don't want to have that filth in the markup :-S There has to be a way.

Thanks in advance for any advice on this.

Upvotes: 0

Views: 186

Answers (1)

Lee이민규
Lee이민규

Reputation: 213

React "component instance" will be not destroyed until unmount. So the state should be changed in component's life-cycle.

See also https://facebook.github.io/react/docs/react-component.html#componentwillreceiveprops

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

class Stats extends Component {
  constructor(props) {
    super(props);

    this.state = {
      totalPlayers: props.players.length,
      totalPoints: props.players.reduce((total, player) => {
        return total + player.score;
      }, 0)
    };
  }

  componentWillReceiveProps(nextProps) {
    const {players} = nextProps;

    this.setState({
      totalPlayers: players.length,
      totalPoints: players.reduce((total, player) => {
        return total + player.score;
      }, 0)
    });
  }

  render() {
    return (
      <table className="stats">
        <tbody>
          <tr>
            <td>Players:</td>
            <td>{this.state.totalPlayers}</td>
          </tr>
          <tr>
            <td>Total Points:</td>
            <td>{this.state.totalPoints}</td>
          </tr>
        </tbody>
      </table>
    );
  }
}

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

export default Stats;

And in this case, I recommend using only props.

getTotalCount() { return this.props.players.length; }

calcTotalPoints() {
    return this.props.players.reduce((total, player) => {
        return total + player.score;
    }, 0)
}

render() {
    <div>
        <div>{this.getTotalCount()}</div>
        <div>{this.calcTotalPoints()}</div>
    <div>
}

Upvotes: 1

Related Questions