reactjs - 改变状态不会重新渲染组件

时间:2016-11-25 02:46:27

标签: reactjs

我使用地图功能从一组对象创建游戏板,并在点击时调用setState以使游戏发生。我可以成功更新状态,但视图不会更新,直到我执行不同的操作。我猜测问题在于map函数如何将props传递给子元素(Cell),但我无法弄清楚我做错了什么。

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

3 个答案:

答案 0 :(得分:0)

move函数中的循环是逐个循环的,因此在到达setState的行之前会抛出错误。该方法的前两行只是在变量this.state.board中指定newBoard的引用。因此,您正在“更新”您的状态,但未正确使用setState。这就是为什么电路板会在您的下一次点击时更新,这会在setState中调用handleClick

1)你应该修改状态的深层副本而不是状态本身

2)修复for循环中的off-by-one-error

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
    ...
}

答案 1 :(得分:0)

请使用

 if(var i = 0;i < arr.length;i++)

,而不是

if(var i = 1;i<=arr.length;i++){}

在你的问题中,当你的移动函数中的j等于board.length时,它会出错。 在代码中更改两个if循环。 我希望我能提供帮助。

  var board = [];
var width = 2;
var height = 3;
var size = width * height;


for (var i=0; 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=0;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 key = {item.id} fuck={item.id} 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')); 
 .dead{

width:10px;height:10px;border:1px solid red;margin:3px;

      }
      .alive{
        border:1px solid green;margin:3px;width:10px;height:10px;
      }
<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="app"></div>

答案 2 :(得分:0)

你正在改变this.stateReact docs指定这是禁忌。在GameBoard组件中,我重写了您的handleClick()move()函数,看起来更像是这样:

handleClick: function(id) {
  this.setState({
    ...this.state,
    board: [
      ...this.state.board.slice(0, id),
      {
        id,
        status: 'alive'
      },
      ...this.state.board.slice(id + 1)
    ]
  });
},

move: function() {
  this.setState({
    ...this.state,
    board: this.state.board.map((place, id, board) => {
      const nextPlace = board[id + 1];
      if (nextPlace.status === 'alive') {
        return {
          ...place,
          status: 'alive'
        };
      } else if (place.status === 'alive') {
        return {
          ...place,
          status: 'dead'
        };
      }
      return place;
    })
  });
},

注意:这些函数使用一些新的Javascript功能,如spread运算符(...place)和箭头函数。当您尝试运行此代码时,这些功能可能会导致语法错误。