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
}