最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Recursive Function in React.JS Component class - Stack Overflow

programmeradmin0浏览0评论

I have a problem with a project that uses React. It's a tic tac toe game and I am trying to implement the puter player.

When the human player clicks on one square (Field-ponent) this triggers the updateField() function in the Board-ponent. It also calls the puterMove() function which in turn calls the minimax() function.

The minimax function is recursivly defined. However, I get the following error message, when the code execution reaches the line where minimax(possibleGame) is called.

[ERROR] react-dom Uncaught TypeError: Cannot read property 'minimax' of undefined

Apparently the function is unknown, which I don't understand because I bind it to the Board scope.

class Field extends React.Component{

  render(){
    return(
      <button className='field' onClick={this.props.clickFunc}>{this.props.value}</button>
    );
  }
}

class Board extends React.Component{
  constructor(){
    super();
    this.state = {
      fields: Array(9).fill(null),
    };
    this.minimax = this.minimax.bind(this);
    this.updateField = this.updateField.bind(this);
    thisputerMove = thisputerMove.bind(this);
  }

  updateField(id){
    const fields = this.state.fields.slice();

    // only proceed if field hasn't been taken yet
    if (fields[id] == null && this.props.winner == null){
      fields[id] = this.props.playerSymbol;
      this.setState({fields: fields});

      thisputerMove(fields);
    } 
  }

// check if somebody won the game
  checkWinner(squares){
    const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;

  }

  puterMove(fields){
    var puterSymbol = (this.props.playerSymbol == "X" ? "O" : "X");
    var game = {fields: fields,turn:puterSymbol};
    var result = this.minimax(game);
    var newBoard = fields.slice();
    newBoard[result[1]] = puterSymbol;
    this.setState({fields: newBoard}); 

    }

  score(game){
    // check for winner
    var result = this.checkWinner(game.fields);
    if (result !== null){
      if (result == this.props.playerSymbol){
        return -10;
      } else{
        return 10;
      }
    }else{
      return 0;
    }
  }

  minimax(game) {
    var score = this.score(game);
    if (score !== 0){
      return score;
    }
    var scores = [];
    var moves = [];

    game.fields.forEach(function(value,index,arr){
      if (value==null){
        var newField = arr.slice();
        newField[index] = game.turn;

        var nextTurn = (game.turn == "X" ? "O" : "X");
        var possibleGame = {fields: newField,turn:nextTurn};
        var result = this.minimax(possibleGame);
        scores.push(result[0]);
        moves.push(index);
      }
    }); 
    if (game.turn == this.props.playerSymbol){
      var max_ind = scores.indexOf(Math.max(...scores));
      return [scores[max_ind],moves[max_ind]];
    } else{
      var min_ind = scores.indexOf(Math.min(...scores));
      return [scores[min_ind],moves[min_ind]];
    }   
  }

  render(){
    var fields = this.state.fields;

    // check if somebody won

    const winner = this.checkWinner(fields);
    if (winner){
      this.props.declareWinner(winner);
    }


    return (
      <div className="animated fadeInUp board-container">
        <div className='row'>
          <Field value={fields[0]} clickFunc={() => this.updateField(0)} />
          <Field value={fields[1]} clickFunc={() => this.updateField(1)}/>
          <Field value={fields[2]} clickFunc={() => this.updateField(2)}/>
        </div>
        <div className='row'>
          <Field value={fields[3]} clickFunc={() => this.updateField(3)} />
          <Field value={fields[4]} clickFunc={() => this.updateField(4)}/>
          <Field value={fields[5]} clickFunc={() => this.updateField(5)}/>
        </div>
        <div className='row'>
          <Field value={fields[6]} clickFunc={() => this.updateField(6)}/>
          <Field value={fields[7]} clickFunc={() => this.updateField(7)}/>
          <Field value={fields[8]} clickFunc={() => this.updateField(8)}/>
        </div>
      </div>
      )
  }
}

The whole code can be tested here:

I also tried to e up with a MWE () However, this example works for some reason.

Thank you for your help!

I have a problem with a project that uses React. It's a tic tac toe game and I am trying to implement the puter player.

When the human player clicks on one square (Field-ponent) this triggers the updateField() function in the Board-ponent. It also calls the puterMove() function which in turn calls the minimax() function.

The minimax function is recursivly defined. However, I get the following error message, when the code execution reaches the line where minimax(possibleGame) is called.

[ERROR] react-dom Uncaught TypeError: Cannot read property 'minimax' of undefined

Apparently the function is unknown, which I don't understand because I bind it to the Board scope.

class Field extends React.Component{

  render(){
    return(
      <button className='field' onClick={this.props.clickFunc}>{this.props.value}</button>
    );
  }
}

class Board extends React.Component{
  constructor(){
    super();
    this.state = {
      fields: Array(9).fill(null),
    };
    this.minimax = this.minimax.bind(this);
    this.updateField = this.updateField.bind(this);
    this.puterMove = this.puterMove.bind(this);
  }

  updateField(id){
    const fields = this.state.fields.slice();

    // only proceed if field hasn't been taken yet
    if (fields[id] == null && this.props.winner == null){
      fields[id] = this.props.playerSymbol;
      this.setState({fields: fields});

      this.puterMove(fields);
    } 
  }

// check if somebody won the game
  checkWinner(squares){
    const lines = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6],
  ];
  for (let i = 0; i < lines.length; i++) {
    const [a, b, c] = lines[i];
    if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
      return squares[a];
    }
  }
  return null;

  }

  puterMove(fields){
    var puterSymbol = (this.props.playerSymbol == "X" ? "O" : "X");
    var game = {fields: fields,turn:puterSymbol};
    var result = this.minimax(game);
    var newBoard = fields.slice();
    newBoard[result[1]] = puterSymbol;
    this.setState({fields: newBoard}); 

    }

  score(game){
    // check for winner
    var result = this.checkWinner(game.fields);
    if (result !== null){
      if (result == this.props.playerSymbol){
        return -10;
      } else{
        return 10;
      }
    }else{
      return 0;
    }
  }

  minimax(game) {
    var score = this.score(game);
    if (score !== 0){
      return score;
    }
    var scores = [];
    var moves = [];

    game.fields.forEach(function(value,index,arr){
      if (value==null){
        var newField = arr.slice();
        newField[index] = game.turn;

        var nextTurn = (game.turn == "X" ? "O" : "X");
        var possibleGame = {fields: newField,turn:nextTurn};
        var result = this.minimax(possibleGame);
        scores.push(result[0]);
        moves.push(index);
      }
    }); 
    if (game.turn == this.props.playerSymbol){
      var max_ind = scores.indexOf(Math.max(...scores));
      return [scores[max_ind],moves[max_ind]];
    } else{
      var min_ind = scores.indexOf(Math.min(...scores));
      return [scores[min_ind],moves[min_ind]];
    }   
  }

  render(){
    var fields = this.state.fields;

    // check if somebody won

    const winner = this.checkWinner(fields);
    if (winner){
      this.props.declareWinner(winner);
    }


    return (
      <div className="animated fadeInUp board-container">
        <div className='row'>
          <Field value={fields[0]} clickFunc={() => this.updateField(0)} />
          <Field value={fields[1]} clickFunc={() => this.updateField(1)}/>
          <Field value={fields[2]} clickFunc={() => this.updateField(2)}/>
        </div>
        <div className='row'>
          <Field value={fields[3]} clickFunc={() => this.updateField(3)} />
          <Field value={fields[4]} clickFunc={() => this.updateField(4)}/>
          <Field value={fields[5]} clickFunc={() => this.updateField(5)}/>
        </div>
        <div className='row'>
          <Field value={fields[6]} clickFunc={() => this.updateField(6)}/>
          <Field value={fields[7]} clickFunc={() => this.updateField(7)}/>
          <Field value={fields[8]} clickFunc={() => this.updateField(8)}/>
        </div>
      </div>
      )
  }
}

The whole code can be tested here: https://codepen.io/miga89/pen/pPqmra

I also tried to e up with a MWE (https://codepen.io/miga89/pen/XRQBjP) However, this example works for some reason.

Thank you for your help!

Share Improve this question asked May 28, 2017 at 11:56 miga89miga89 4381 gold badge6 silver badges18 bronze badges 1
  • 2 game.fields.forEach((value,index,arr) => { – dfsq Commented May 28, 2017 at 11:58
Add a ment  | 

2 Answers 2

Reset to default 2

The value of this depends on how and where the function is being called.

In most cases, the value of this is determined by how a function is called. It can't be set by assignment during execution, and it may be different each time the function is called. ES5 introduced the bind method to set the value of a function's this regardless of how it's called, and ES2015 introduced arrow functions whose this is lexically scoped (it is set to the this value of the enclosing execution context). https://developer.mozilla/en/docs/Web/JavaScript/Reference/Operators/this

You need to explicitly bind this to be used consistently inside the function.

game.fields.forEach(function(value,index,arr){
  if (value==null){
    var newField = arr.slice();
    newField[index] = game.turn;

    var nextTurn = (game.turn == "X" ? "O" : "X");
    var possibleGame = {fields: newField,turn:nextTurn};
    var result = this.minimax(possibleGame);
    scores.push(result[0]);
    moves.push(index);
  }
}.bind(this));

or, use arrow functions:

game.fields.forEach((value,index,arr) => {
  if (value==null){
    var newField = arr.slice();
    newField[index] = game.turn;

    var nextTurn = (game.turn == "X" ? "O" : "X");
    var possibleGame = {fields: newField,turn:nextTurn};
    var result = this.minimax(possibleGame);
    scores.push(result[0]);
    moves.push(index);
  }
});

An arrow function expression has a shorter syntax than a function expression and does not bind its own this, arguments, super, or new.target. These function expressions are best suited for non-method functions, and they cannot be used as constructors. https://developer.mozilla/en/docs/Web/JavaScript/Reference/Functions/Arrow_functions

Maintain the rules of recursion: suppose we have a parent child tree then in the bellow you will find an example

In the constructor function:

this.checkSlug = this.checkSlug.bind(this);
this.onViewToBar = this.onViewToBar.bind(this);

your recursive function example:

  checkSlug = (slug, parent) => {
    for (let i = 0; i < parent.length; i++) {
      if (parent[i].children && parent[i].slug !== slug) {
        const value = this.checkSlug(slug, parent[i].children);
        if (value){
          return value;
        }
      } else {
        if (parent[i].slug === slug) {
          return parent[i];
        }
      }
    }
  }

now my caller function

 onViewToBar({ slug }) {
    this.setState({ slug: slug });
    const { cursor, data } = this.state;

    let node =this.checkSlug(slug, data);
  }
```
发布评论

评论列表(0)

  1. 暂无评论