0

I am trying to create a simple tic tac toe gaming using functional components. However, I seem to have an issue where my state is always initially one step behind on the first click of a square. I know this question has been asked many times and I have read through a lot of posts, but I am still struggling to get my state in sync. I also noticed that when I call my board reset function instead of starting with X as it should it tries to start with O which technically should not be possible since it is being set back to its initial state. Any help or suggestions anyone has would be greatly appreciated!

App.js

import React, { useState, useEffect } from 'react';

import Board from './Board/Board';
import { calculateWinner } from '../shared/utils';
import { DEFAULT_BOARD } from '../shared/constants';
import './App.css';

const App = () => {
    const [state, setState] = useState({
        squares: Array(9).fill(null),
        stepNumber: 0,
        xIsNext: true,
        status: "Next player: X",
        moves: 0
    });

    useEffect(() => {
        const winner = calculateWinner(state.squares);
        if(winner) {
            setState((prevState) => ({
                ...prevState,
                status: 'Winner: ' + winner
            }));
        }
        if(state.moves >= 9) {
            setState((prevState) => ({
                ...prevState,
                status: 'Its a draw!'
            }));
        }
    }, [state.squares, state.moves]);

    const resetBoard = () => {
        setState({...DEFAULT_BOARD});
    }

    const handleClick = (i) => {
        const tempSquares = state.squares.slice();
        if (calculateWinner(tempSquares) || tempSquares[i]) {
          return;
        }
        let temp = !state.xIsNext
        tempSquares[i] = state.xIsNext ? 'X' : 'O';
        setState((prevState) => ({
            ...prevState,
            xIsNext: temp,
            moves: state.moves + 1,
            squares: tempSquares,
            status: 'Next player: ' + (state.xIsNext ? "X" : "O")
        }))
    }
    
    return (
        <div className="game">
            <Board handleClick={handleClick} status={state.status} squares={state.squares} resetBoard={resetBoard}/>
        </div>
    );
}

export default App;

Board.js


import Square from '../Square/Square';
import './Board.css';

const Board = ({status, handleClick, squares, resetBoard}) => {
    const renderSquare = (i) => {
        return (
            <Square value={squares[i]} onClick={handleClick} index={i} />
          );
    }

    return (
        <Fragment>
            <div className="board">
                <div className="status">{status}</div>
                <div className="board-grid">
                    {squares.map((value, index) => {
                        return (
                            <div key={index}>
                                {renderSquare(index)}
                            </div>
                        )
                    })}
                </div>
                <button onClick={resetBoard}>Reset Board</button>
            </div>
        </Fragment>
    )
}

export default Board;

Square.js

import React from 'react';

import './Square.css'

const Square = ({onClick, value, index}) => {
    return (
        <button className="square" onClick={() => onClick(index)}>
            {value}
        </button>
    )
}

export default Square;

util.js

export const calculateWinner = (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;
}

constants.js

export const DEFAULT_BOARD = {
    squares: Array(9).fill(null),
    xIsXNext: true,
    status: "Next player: X",
    moves: 0
}
Austin S
  • 145
  • 1
  • 10
  • Does this answer your question? [useState set method not reflecting change immediately](https://stackoverflow.com/questions/54069253/usestate-set-method-not-reflecting-change-immediately) – freefall Jan 26 '21 at 22:44
  • It is a much more detailed explanation that is helpful, but I have tried setting the state of the status within the useEffect as well and it still had the same issue. I feel like it may be the order in which I am setting it and updating, but I feel like I have tried almost everything to no avail . – Austin S Jan 26 '21 at 22:53

0 Answers0