Search code examples
reactjsreact-hooksnext.jssetstate

setState is delayed by one state


Don't know what I am doing wrong here, maybe it is a rookie mistake.

I am trying to recreate chess.

My state is delayed by one.. i.e. when I click on square to show possible moves of piece, the possible moves will appear but only after my second click, they will show on the right spot. And for the 3rd click the state from 2nd click will appear on the right spot and so on. What am I doing wrong here? I am sending few code examples from my next.js app.

This is my Square component

export default function Square({
  x,
  y,
  color,
  content,
  contentClr,
  handleClick,
}) {
  const col = color == 'light' ? '#F3D9DC' : '#744253';

  return (
    <div
      className='square'
      x={x}
      y={y}
      style={{ backgroundColor: col }}
      onClick={() => handleClick(x, y)}
    >
      {content != '' && (
        <h1 style={{ color: contentClr == 'light' ? 'white' : 'black' }}>
          {content}
        </h1>
      )}
    </div>
  );
}

This is my Board component

import { useState, useEffect } from 'react';
import Square from './square';

import styles from '../styles/Board.module.css';
import rookMoves from './possibleMoves/rookMoves';

export default function Board({ initialBoard, lightTurn, toggleTurn }) {
  let size = 8;
  const [board, setBoard] = useState(initialBoard);
  const [possibleMoves, setPossibleMoves] = useState([]);
  //possibleMoves .. each possible move is represented as x, y coordinates.

  useEffect(() => {
    console.log('board state changed');
  }, [board]);

  useEffect(() => {
    console.log('possible moves state changed');
    console.log(possibleMoves);
    renderPossibleMoves();
  }, [possibleMoves]);

  const playerClickOnPiece = (x, y) => {
    let pos = getPossibleMoves(x, y, rookMoves, [left, right, up, down]);
    setPossibleMoves(pos);
  };

  //where to is object that has functions for directions in it
  const getPossibleMoves = (x, y, movePiece, whereTo) => {
    let posMoves = [];
    for (let i = 0; i < whereTo.length; i++) {
      posMoves = posMoves.concat(movePiece(board, x, y, whereTo[i]));
    }

    console.log(posMoves);
    return posMoves;
  };

  const renderPossibleMoves = () => {
    console.log('board from renderPossibleMoves ', board);
    let newBoard = board;
    for (let i = 0; i < possibleMoves.length; i++) {
      newBoard[possibleMoves[i].y][possibleMoves[i].x] = 10;
    }
    setBoard(newBoard);
  };

  const squareColor = (x, y) => {
    return (x % 2 == 0 && y % 2 == 0) || (x % 2 == 1 && y % 2 == 1)
      ? 'light'
      : 'dark';
  };

  const handleClick = (x, y) => {
    if (lightTurn && board[y][x] > 0) {
      console.log(
        `show me possible moves of light ${board[y][x]} on ${x} ${y}`
      );
      playerClickOnPiece(x, y);
    } else if (!lightTurn && board[y][x] < 0) {
      console.log(`show me possible moves of dark ${board[y][x]} on ${x} ${y}`);
      playerClickOnPiece(x, y);
    } else if (board[y][x] == 'Pm') {
      playerMove();
    }
  };

  return (
    <>
      <div className='board'>
        {board.map((row, y) => {
          return (
            <div className='row' key={y}>
              {row.map((square, x) => {
                return (
                  <Square
                    x={x}
                    y={y}
                    key={x}
                    color={squareColor(x, y)}
                    content={square}
                    contentClr={square > 0 ? 'light' : 'dark'}
                    handleClick={handleClick}
                    playerMove={toggleTurn}
                  />
                );
              })}
            </div>
          );
        })}
      </div>
    </>
  );
}

import { useState } from 'react';
import Board from './board';

let initialBoard = [
  [-4, -3, -2, -5, -6, -2, -3, -4],
  [-1, -1, -1, -1, -1, -1, -1, -1],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [1, 1, 1, 1, 1, 1, 1, 1],
  [4, 3, 2, 5, 6, 2, 3, 4],
];

export default function Game() {
  const [lightTurn, setLightTurn] = useState(true);

  const toggleTurn = () => {
    setLightTurn(!lightTurn);
  };

  return (
    <Board
      initialBoard={initialBoard}
      lightTurn={lightTurn}
      toggleTurn={toggleTurn}
    />
  );
}

I am sending an example that shows only possible moves with rook but if u click on any light piece.


Solution

  • @juliomalves's comment helped! I was mutating state directly in renderPossibleMoves. Spread operator solved the issue.

    from

    let newBoard = board;
    

    to

    let newBoard = [...board];