this.State未定义

时间:2017-11-26 18:06:10

标签: javascript reactjs ecmascript-6

我无法弄清楚为什么this.state未定义。

首次点击开始按钮updateBoard()并调用startGame()时。 第一个时间startGame()被称为运行正常,并在结束时更改状态然后重新呈现电路板。

调用重新渲染componentDidUpdate()并启动setTimeout()后,在1000毫秒后调用this.startGame。运行 startGame()后,我就会......

TypeError: this.state is undefined代表if()函数中的第一个startGame()

这是codepen

上的代码

这是来自codepen的代码

const Board = (props) => {

  var size = props.boardSize;
  var row = 0;

  const toggleCell = (el) => {

    var ele = document.getElementById(el);

    if(ele.className == 'off'){
      ele.className = 'on';
    }
    else{
      ele.className = 'off';
    }

  }

  const cols = (rows) => {

      if(props.state.board[0][0] != undefined){

          return props.state.board[row].map((data2, index2) => {

            var id = '' + row + '-' + index2;
            return (<td onClick={toggleCell.bind(this, id)} id={`${rows}-${index2}`} className={`${data2}`}> </td>);
          });
      }
      else{

          return props.state.board.map((data2, index2) => {

            var rand = Math.floor((Math.random() * 100) + 1);
            var choice = '';

            if(rand<22){
             choice = 'on';
            }
            else{
                choice = 'off';
            }

            var id = '' + row + '-' + index2;

            return (<td onClick={toggleCell.bind(this, id)} id={`${rows}-${index2}`} className={`${choice}`}> </td>);
          });
      }


  }

  const genCells = props.state.board.map((data, index) => {
      row = index;
            return (<tr id={`row${index}`} row={index}> {cols(index)} </tr>);
      });

  return (
    <div>
      <div>
        <h1 id="title"> The Game of Life </h1>
        <h5 id="generations">Gen: {props.state.gens}</h5>
        <table id="life">
          {genCells}
        </table>
      </div>

      <div id="buttons">
        <button onClick={() => {props.createBoard(20)}}  className="btn btn-primary sizes"> 20 x 20 </button>
        <button onClick={() => {props.createBoard(30)}}  className="btn btn-primary sizes"> 30 x 30 </button>
        <button onClick={() => {props.createBoard(40)}} className="btn btn-primary sizes"> 40 x 40 </button>
      </div>
      <div id="options">
        <button onClick={() => {props.toggleRun(true); props.startGame();}}     className="btn btn-success sizes"> Start </button>
        <button onClick={() => {props.toggleRun(false)}}                    className="btn btn-warning sizes"> Pause </button>
        <button onClick={() => {props.clearBoard()}}                        className="btn btn-danger sizes">  Reset </button>
      </div>

    </div>

    );
}

class App extends React.Component {
    constructor(props){
        super(props);
        this.state={
            newGame: true,
            gens: 0,
            nextGen: [],
            size: 0,
            board: [],
            running: false
        }
      }

    createBoard(dim) {

        //If a number is given (60, 80, 120), generate the board
        if(dim>0){
            //create empty 'board'-Array
            var board = [];
            //create rows and col in 'board'-Array
            for(var i=0;i<dim;i++){
              let temp = new Array(dim);
              board.push(temp);
            }
            //set new array to 'board'-State(Array)
            this.setState({
              board: board, 
              size: dim
            });
        }
        else{

        }


    }

    updateBoardState(newBoard){
      var board = this.state.board;
      var length = board.length;

      if(newBoard == undefined){
          var newBoard = board;

          for(var i=0;i<length;i++){
              for(var j=0;j<length;j++){

                  newBoard[i][j] = document.getElementById(''+i+'-'+j).className;

              }
            }


      }

      if(newBoard != undefined){

            for(var i=0;i<length;i++){
              for(var j=0;j<length;j++){

                  document.getElementById(''+i+'-'+j).className = newBoard[i][j];

              }
            }



            this.setState({board:newBoard, 
                    gens: this.state.gens++});
        }

      //console.log("board Updated!");
    }

    toggleRun(boo){
        this.updateBoardState();
        this.setState({running: boo});
    }

    clearBoard(){
        var ons = document.getElementsByClassName('on');
        //console.log(ons);
        for(var i=0;i<ons.length;i++){
          ons[i].className='off';
          i--;
        }
    } 

    startGame(){


        if(this.state.running == true){
            var board = this.state.board;
            var size = this.state.size;
            var newBoard = board;

            for(var i=0;i<size;i++){
                for(var j=0;j<size;j++){

                    //Declarations
                    let aliveNeighbours = 0;

                    var target = document.getElementById(''+i+'-'+j);

                    //Neigbours

                    var neighblocks = new Array();




                    neighblocks.push(document.getElementById(''+(i-1)+'-'+(j-1)));
                    neighblocks.push(document.getElementById(''+(i-0)+'-'+(j-1)));
                    neighblocks.push(document.getElementById(''+(i+1)+'-'+(j-1)));

                    neighblocks.push(document.getElementById(''+(i-1)+'-'+(j-0)));
                    neighblocks.push(document.getElementById(''+(i+1)+'-'+(j-0)));

                    neighblocks.push(document.getElementById(''+(i-1)+'-'+(j+1)));
                    neighblocks.push(document.getElementById(''+(i-0)+'-'+(j+1)));
                    neighblocks.push(document.getElementById(''+(i+1)+'-'+(j+1)));


                    //Check if Neighbour is alive 
                    //(results in `aliveNeighbours`)
                    neighblocks.forEach(function(el){
                        if(el != null){
                            if(el.className == 'on'){
                                aliveNeighbours++;
                            }
                        }
                        else{

                        }


                    });

                    //console.log("cell#"+i+"-"+j+" alive neighbours = " + aliveNeighbours);



                    //RULES
                    if(aliveNeighbours<2){
                        //die of isolation
                        newBoard[i][j] = 'off';
                        //console.log("neighbour killed!");

                    }
                    if(aliveNeighbours == 2){
                        //live on
                        if(target.className == 'on'){
                            newBoard[i][j] = 'on';
                            //console.log("surviver");
                        }
                    }
                    if(aliveNeighbours > 3){
                        //die of over population
                        newBoard[i][j] = 'off';
                        //console.log("death!");

                    }
                    if(aliveNeighbours == 3){
                        //live on / give birth
                        newBoard[i][j] = 'on';
                        //console.log("its a boy!");
                    }

                }
            }


       for(let a=0;a<size;a++){
          for(let b=0;b<size;b++){
            var eel = document.getElementById(''+a+'-'+b);
            eel.className = newBoard[a][b];
          }
      }


      //this.updateBoardState(newBoard);

        }        

    }

    componentWillMount(){
        if(this.state.newGame){
            this.createBoard(8);
            this.setState({newGame: false, 
                    running: true});
        }
    }

    componentDidUpdate(){
    var startGamee = this.startGame;
    console.log(this.startGame);

            setTimeout(this.startGame, 1000);
    }

  render(){

    return (
      <div>

        <div>
          <Board    createBoard={this.createBoard.bind(this)} 
                            toggleRun={this.toggleRun.bind(this)} 
                            clearBoard={this.clearBoard.bind(this)} 
                            state={this.state}
                            updateBoardState={this.updateBoardState.bind(this)} 
                            startGame={this.startGame.bind(this)}
                    />
        </div>

      </div>
    )

  }

}

ReactDOM.render(<App />, document.getElementById('root'));

3 个答案:

答案 0 :(得分:2)

以下是您问题的最小例子:

&#13;
&#13;
class Test {
  constructor() {
    this.name = "test"
  }
  startGame() {
    console.log(this.name)
  }
  delay() {
    setTimeout(this.startGame, 1000)
  }
}

let t = new Test
t.startGame() // <- this works
t.delay()    // <- probably not what you're hoping for
&#13;
&#13;
&#13;

您将在窗口对象中看到delay()日志undefined或其他内容。这是因为this由函数的调用位置决定。在setTimeout的情况下,this不再指向您的班级,因为它没有像t.startGame()那样被调用。 setTimeout正在调用它,this指的是setTimeout认为this的所有内容。

一种简单的解决方法是在超时时使用箭头功能:

&#13;
&#13;
class Test {
  constructor() {
    this.name = "test"
  }
  startGame() {
    console.log(this.name)
  }
  delay() {
    setTimeout(() => this.startGame(), 1000)
  }
}

let t = new Test
t.startGame()
t.delay()
&#13;
&#13;
&#13;

另一种方法是使用以下函数传递一个显式绑定到您的类的函数:

delay(){
    setTimeout(this.startGame.bind(this), 1000)
}

答案 1 :(得分:2)

componentDidMount()方法中,您需要绑定this.startGame()才能访问状态。见下文:

componentDidUpdate() {
    var startGame = this.startGame;
    console.log(this.startGame);

    setTimeout(this.startGame.bind(this), 1000); // bound to `this`
}

这是因为setTimeout()是异步的。

答案 2 :(得分:0)

您必须将startGame()绑定到构造函数中的App组件,因此该函数具有正确的词法范围。在App构造函数中:

constructor(props){
        super(props);
        this.state={
            newGame: true,
            gens: 0,
            nextGen: [],
            size: 0,
            board: [],
            running: false
        }
        this.startGame = this.startGame.bind(this)
      }