How can I set the state from the props passed to the component in react?

I have this simple component, the initialPlayers

props are passed to the component App

.

import React from 'react';
import ReactDOM from 'react-dom';

var PLAYERS = [
  {
    name: "xyz",
    score: 123
  }
];

// App component
class App extends React.Component {

 constructor() {
   super();
 }

 componentDidMount() {
   this.state = {
     players: this.props.initialPlayers
   }
 }

 render() {    
   return(
     <div>
       <Header players={this.state.players} />
     </div>
   );
 }
}

// Render component
ReactDOM.render(<App initialPlayers={ PLAYERS }/>, 
document.getElementById('root'));

      

Take this error in console and won't be able to pass the value Header

as {this.state.players}

. Any idea?

Uncaught TypeError: Cannot read property 'players' of null
at App.render (bundle.js:14379)
at bundle.js:20173
at measureLifeCyclePerf (bundle.js:19452)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (bundle.js:20172)
at ReactCompositeComponentWrapper._renderValidatedComponent (bundle.js:20199)
at ReactCompositeComponentWrapper.performInitialMount (bundle.js:19739)
at ReactCompositeComponentWrapper.mountComponent (bundle.js:19635)
at Object.mountComponent (bundle.js:4667)
at ReactCompositeComponentWrapper.performInitialMount (bundle.js:19748)
at ReactCompositeComponentWrapper.mountComponent (bundle.js:19635)

      

+3


source to share


3 answers


Move the line of players settings into your constructor ():



constructor(props) {
  super(props);
  this.state = {
    players: this.props.initialPlayers
  };
}

      

+3


source


You want to use componentWillMount

because it executes before , the component renders first - compare that with the descriptioncomponentDidMount



var PLAYERS = [
  { name: "xyz", score: 123 },
  { name: 'abc', score: 111 },
  { name: 'def', score: 222 }
];

const Header = ({players}) =>
  <ul>{players.map(({name,score}) =>
    <li><span>{name}</span><span>{score}</span></li>)}</ul>

// App component
class App extends React.Component {

  // get rid of constructor if you're not doing anything with it
  // constructor() { ... }

  // use componentWillMount instead of componentDidMount
  componentWillMount() {
    this.setState({
      players: this.props.initialPlayers
    })
  }

  // don't wrap everything in a div if it not necessary
  render() {    
    return <Header players={this.state.players} />
  }
}

// Render component
ReactDOM.render(<App initialPlayers={ PLAYERS }/>, 
document.getElementById('root'));
      

span {
  display: inline-block;
  font-weight: bold;
  margin-right: 1em;
}

span ~ span {
  font-weight: normal;
}
      

<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="root"></div>
      

Run codeHide result


+1


source


You first need to initialize yours state

, otherwise you will get an error updating its value, then you have to use a method setState

to change it (this is the recommended update way state

in react)

import React from 'react';
import ReactDOM from 'react-dom';

var PLAYERS = [
  {
    name: 'xyz',
    score: 123
  }
];

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      players: []
    };
  }
  componentDidMount() {
    this.setState({
      players: this.props.initialPlayers
    });
  }
  render() {
    return(
      <div>
        <ul>
          {this.renderPlayers()}
        </ul>
      </div>
    );
  }
  renderPlayers() {
    return this.state.players.map((player, index) =>
      <li key={index}>{`name: ${player.name} - score: ${player.score}`}</li>
    );
  }
}

ReactDOM.render(
  <App initialPlayers={ PLAYERS }/>,
  document.getElementById('root')
);

      

0


source







All Articles