Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'setState' of useState hook occurs twice by one call

There is a board with squares their value relies on an array, it is handled with useState hook. Every click should raise the value by one, but unfortunately, it raises it by two (except the first click).

My questions are:

(1) Why is it happen, (2) how to avoid it, and, in general, (3) is there a better way to handle such an array with hooks.

let emptyBoard = Array.from({ length: parseInt(props.rows, 10) }, () =>
    new Array(parseInt(props.columns, 10)).fill(0)
  );

  const [squaresValues, setSquaresValue] = useState(emptyBoard);

  function onClick(id) {
    const [rowIndex, cellIndex] = id;
    console.log("the " + id + " square was clicked");
    setSquaresValue(prevValues => {      
      let newBoard = [...prevValues];
      console.log("before: " + newBoard[rowIndex][cellIndex]);
      newBoard[rowIndex][cellIndex] = newBoard[rowIndex][cellIndex] + 1;
      console.log("after: " + newBoard[rowIndex][cellIndex]);
      return newBoard;
    }
    );
  }

The log:

the 3,0 square was clicked 
before: 0 
after: 1 
the 3,0 square was clicked 
before: 1 
after: 2 
before: 2 
after: 3 

As can be seen, from the second click the value is raised twice by every click.

like image 599
yoni Avatar asked Oct 31 '25 11:10

yoni


1 Answers

You were still mutating state, if you have pure components then they won't re render when mutating. Doing the full state copy with JSON.parse is a bad idea if you have pure components because everything will be re rendered.

let newBoard = [...prevValues];
newBoard[rowIndex] = [...newBoard[rowIndex]];
newBoard[rowIndex][cellIndex] =
newBoard[rowIndex][cellIndex] + 1;
like image 111
HMR Avatar answered Nov 03 '25 03:11

HMR



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!