2

I have read the react documentation and examples here: When to use React setState callback

I am trying to use a callback to get setState() to update immediately. But my background color, or squaresColor[i] is still not updating.

The console.log(squaresColor[i]) is outputting undefined: green. The green is correct but the key undefined.

I tried to.. setState(squaresColor: squaresColor), but upon pressing the enter key, the background still does not change. For whatever reason, I am using the same setState in a different method, handleClick(). This is being called asynchronously and it is working fine.

So why is the background color not updating in handleKeyPress? Should i not be using state to make this update?

My Code :

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';



/*stop clicking ability after clicking a problem (prevent default) and enable it when submit is done.*/

/*  let btnStyle = {
        backgroundColor: 'green'
    } */

/*we also changed onClick={() => props.onClick()} to just onClick={props.onClick},
 as passing the function down is enough for our example. Note that onClick={props.onClick()}
 would not work because it would call props.onClick immediately instead of passing it down.*/



class  Square extends React.Component
{
    render()
    {
        return (
                 <button className="square" onClick = {this.props.onClick} style = {{backgroundColor: this.props.backgroundColor}}>
                    {this.props.value}
                    {this.props.txt}
                </button>
            );
    }

}

class Board extends React.Component
{
    constructor(props)
    {
    super(props);
    this.state =
    {
      squares: Array(25).fill(null),
      xIsNext: true,
      squaresColor: Array(25).fill('null'),
      num1: generateRandomNumber(),
      num2: generateRandomNumber(),
      ans:  function(a,b){return(a * b);},
      sqTxt: Array(25).fill(null),
      next: true
    };

    //this.handleKeyPress = this.handleKeyPress.bind(this);

  }

  handleClick(i)
  {
    const squares = this.state.squares.slice();           // makes a mutable copy of the array
    const squaresColor = this.state.squaresColor.slice(); // makes a mutable copy of the array
    const sqTxt = this.state.sqTxt.slice(); // makes a mutable copy of the array
    if (!(this.state.next) || squares[i])
    {
      return;
    }

    squaresColor[i] = 'blue';
    sqTxt[i] =  <input onKeyPress = {(e,i) => this.handleKeyPress(e, i)} className = 'answer-input' type = 'text' name = 'answer' />;


    this.setState({
                    squares:      squares,
                    squaresColor: squaresColor,
                    sqTxt:        sqTxt,
                    next:         false
                  });

    console.log(this.state.squaresColor[i]);

    squares[i] = this.state.num1 + ' X ' + this.state.num2;

    return this.state.next;

  }


  /*When an event is invoked, it will be passed an event object as it's first argument. You can name evt whatever you like. Common names are e evt and event.*/
  handleKeyPress(e, i)
  {
      const userAnswer = parseInt(e.target.value, 10);
      const num1 = this.state.num1;
      const num2 = this.state.num2;
      const correctAnswer = num1 *  num2;
      const squaresColor = this.state.squaresColor.slice();           // makes a mutable copy of the array      

       if(e.key === 'Enter')
      { 
        squaresColor[i] = 'green';

          if(userAnswer === correctAnswer)
          {
              this.setState({
                                /* squaresColor: */ squaresColor,
                                next:         true,
                            }/* ,  function() 
                                {
                                    console.log(this.state.squaresColor);
                                    this.setState(squaresColor: squaresColor);
                                } */);

              //create new numbers for next problem
             this.setState({num1: generateRandomNumber(), num2: generateRandomNumber()});
          }

          else
          {
              console.log('incorrect');
          }  
      }
  }



  renderSquare(i)
  {
    return (
      <Square
        value={this.state.squares[i]}
        onClick = {() => this.handleClick(i)}
        backgroundColor = {this.state.squaresColor[i]}
        txt = {this.state.sqTxt[i]}
      />
    );
  }

  render()
  { 
    return (
      <div className = 'all-rows'>
        <div className="board-row">
          {this.renderSquare(0)}
          {this.renderSquare(1)}
          {this.renderSquare(2)}
          {this.renderSquare(3)}
          {this.renderSquare(4)}
        </div>
        <div className="board-row">
          {this.renderSquare(5)}
          {this.renderSquare(6)}
          {this.renderSquare(7)}
          {this.renderSquare(8)}
          {this.renderSquare(9)}
        </div>
        <div className="board-row">
          {this.renderSquare(10)}
          {this.renderSquare(11)}
          {this.renderSquare(12)}
          {this.renderSquare(13)}
          {this.renderSquare(14)}
        </div>
        <div className="board-row">
          {this.renderSquare(15)}
          {this.renderSquare(16)}
          {this.renderSquare(17)}
          {this.renderSquare(18)}
          {this.renderSquare(19)}
        </div>
        <div className="board-row">
          {this.renderSquare(20)}
          {this.renderSquare(21)}
          {this.renderSquare(22)}
          {this.renderSquare(23)}
          {this.renderSquare(24)}
        </div>
      </div>
    );
  }
}


class Game extends React.Component
{
  render() {
    return (
      <div className="game">
        <div className="gxame-board">
          <Board />
        </div>
        <div className="game-info">
        </div>
      </div>
    );
  }
}


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


function generateRandomNumber()
{
    return Math.floor(Math.random() * 10);
}
Trip
  • 26,756
  • 46
  • 158
  • 277
  • Why are you calling setState in the setState callback ? – Striped Feb 23 '18 at 16:30
  • You could simplify your state update by `this.setState({squaresColor, next: true, num1: generateRandomNumber(), num2: generateRandomNumber()});` – Striped Feb 23 '18 at 16:32
  • can you show how you are setting ur background color. Which log is for which `console` statement. It is quite tough to understand ur question. – Panther Feb 23 '18 at 16:47
  • @Panther Sorry, here is my entire js file. I have edited my original post to include the entire file. Thank you. – Michael Connor Feb 23 '18 at 17:45
  • Remember to rebind this reference in your constructor for your event handlers. `this.handleKeyPress = this.handleKeyPress.bind(this); this.handleClick = this.handleClick.bind(this);` – Kunukn Feb 23 '18 at 18:50
  • @Panther, don't the arrow functions i am using sqTxt[i] = this.handleKeyPress(e, i)} className = 'answer-input' type = 'text' name = 'answer' />; accomplish the same this as binding the method to the instance of this in the constructor? either way, I have bound both onClick and onKeyPress but still receive null for the color – Michael Connor Feb 23 '18 at 19:09
  • @Striped how do I use the callback function to update the state when the click event Listener fires? That is what i was trying to do. I understand you can add a callback to the setState method, but how do you use the callback to synchronously update the state, specifically squareColors[i] when the click event fires? I have tried your second recomendation, but the background of the square still did not change. The entire file has been added to the first post I made for further clarity. Thank you for your help. – Michael Connor Feb 23 '18 at 19:17

1 Answers1

1

You do not need to call setState again. You should rewrite this part like :

if(e.key === 'Enter') { 
  squaresColor[i] = 'green';

  if(userAnswer === correctAnswer) {
      this.setState({
          squaresColor,
          next: true,
          num1: generateRandomNumber(),
          num2: generateRandomNumber()
      }, () => {
          console.log(this.state);
      });
  } else {
      console.log('incorrect');
  }  
}

And do not comment this.handleKeyPress = this.handleKeyPress.bind(this);.

Striped
  • 2,544
  • 3
  • 25
  • 31