Matheus Alves Matheus Alves - 10 days ago 6
React JSX Question

reactjs - changing state doesn't re-render component

I'm using a map function to create a game board from an array of objects and calling setState on click to make the game happen. I can successfuly update the state but the view won't update until I perform a different action. I'm guessing the problem is in how the map function passes props to the child element (Cell), but I can't figure out what I'm doing wrong.

var board = [];
var width = 50;
var height = 30;
var size = width * height;


for (var i=1; i<=size; i++) {
board[i] = {id: i, status: 'dead'};
}

var Cell = React.createClass({
turn: function() {
this.props.turn(this.props.id);
},

render: function() {
return <div id={this.props.id} className={this.props.status} onClick={this.turn}></div>
}
});

var GameBoard = React.createClass({
getInitialState: function() {
return {
board: board
};
},

handleClick: function(id) {
var newBoard = this.state.board;
newBoard[id] = {id: id, status: 'alive'};
this.setState({board: newBoard});
},

move: function() {
var board = this.state.board;
var newBoard = board;
for (var j=1;j<=board.length;j++) {
if (board[j].status == 'alive') {
newBoard[j-1] = {id: j-1, status: 'alive'};
newBoard[j] = {id: j, status: 'dead'};
}
}
this.setState({board: newBoard});
},

render: function() {
var squares = this.state.board.map(function(item){
return <Cell id={item.id} status={item.status} turn={this.handleClick}/>
}.bind(this));
return (
<div>
<h1>Game of Life</h1>
<button className="btn btn-default" onClick={this.move}>Run</button>
<div className='boardContainer'>{squares}</div>
</div>
);
}
});

ReactDOM.render(<GameBoard/>,document.getElementById('app'));


http://codepen.io/Theeeus/pen/YpQzPO?editors=0010

Answer

The loop in your move function is off-by-one, so it throws an error before reaching the line with setState. The first two lines of the method simply assign a reference to this.state.board in the variable newBoard. Therefore, you're "updating" your state, but not correctly with setState. That's why the board updates on your next click, which calls setState in handleClick.

1) You should modify a deep copy of the state instead of the state itself

2) Fix the off-by-one-error in the for loop

var board = this.state.board;
var newBoard = board; // Note, this is a reference to this.state.board. That's bad!
for (var j=1; j<=board.length - 1; j++) { // note, -1
    ...
}
Comments