Following along with the react tutorial to create a tic-tac-toe game, and it works, no problem there. I get why nearly all of it works, apart from the display of the winner.
import { useState } from 'react';
export default function Board() {
const [xIsNext, setXIsNext] = useState(true);
const [squares, setSquares] = useState(Array(9).fill(null));
function handleClick(idx) {
const nextSquares = squares.slice();
if (nextSquares[idx] || calculateWinner(squares)) {
return;
}
nextSquares[idx] = xIsNext ? 'X' : '0';
setSquares(nextSquares);
setXIsNext(!xIsNext);
}
let winner = calculateWinner(squares);
let status = winner
? 'Winner: ' + winner
: 'Next player: ' + (xIsNext ? 'X' : '0');
return (
<>
<div className="status">{status}</div>
<div className="board-row">
<Square val={squares[0]} onSquareClick={() => handleClick(0)} />
<Square val={squares[1]} onSquareClick={() => handleClick(1)} />
<Square val={squares[2]} onSquareClick={() => handleClick(2)} />
</div>
<div className="board-row">
<Square val={squares[3]} onSquareClick={() => handleClick(3)} />
<Square val={squares[4]} onSquareClick={() => handleClick(4)} />
<Square val={squares[5]} onSquareClick={() => handleClick(5)} />
</div>
<div className="board-row">
<Square val={squares[6]} onSquareClick={() => handleClick(6)} />
<Square val={squares[7]} onSquareClick={() => handleClick(7)} />
<Square val={squares[8]} onSquareClick={() => handleClick(8)} />
</div>
</>
);
}
The squares updating is clear – the state is set and the Square
component is updated.
What I don’t get is how status
is recalculated. What triggers that little block of code to set winner
and status
to run again? Is it updating the state? If that is run again, then the definition of xIsNext
and friends must be happening again?
There is some magic there that I’m not getting and it isn’t explained in the tutorial (which otherwise is really clear).
Many thanks for any insight.
3
Answers
Yes. Updates to state trigger a re-render, and a re-render executes the entire
Board()
function again. Functionality in React’s hooks (useState
,useEffect
, etc.) internally manages what it does on subsequent renders/executions, because they are executed on every render.Simple assignment statements in the body of the component are assigned on every render.
Incidentally, this can matter significantly if logic within the component involves heavy processing/calculations/etc. If it’s surprising to a developer that the entire component re-executes, potentially many times, then this could lead to significant performance problems. It’s good to make use of things like
useCallback
anduseMemo
, etc. to mitigate this.In React, the status calculation and update are part of the component’s rendering cycle. Here’s a breakdown of how this works:
Component Rendering and State Updates:
When you call setSquares(nextSquares) or setXIsNext(!xIsNext), React schedules a re-render of the Board component. This means the component function (i.e., Board) is called again, with the updated state values.
Recalculation of status:
During each render, the status and winner variables are recalculated based on the current state values (squares and xIsNext).
The line let winner = calculateWinner(squares); runs every time the Board component renders. Similarly, let status = winner ? ‘Winner: ‘ + winner : ‘Next player: ‘ + (xIsNext ? ‘X’ : ‘0’); is recalculated with each render.
React’s Re-rendering:
When you update the state with setSquares or setXIsNext, React triggers a re-render of the Board component.
During this re-render, React calls the Board function again. This causes the winner and status variables to be recalculated based on the updated state.
You’re seeing is the way React handles state changes and re-renders. Each time the state updates, React re-executes the Board function, so status is recalculated with the new state.
In summary, every time state changes, React re-renders the component, running the Board function again and recalculating status and winner with the updated state. This is why the displayed status is always current with the latest state.
What I don’t get is how status is recalculated. What triggers that little block of code to set winner and status to run again?
Is it updating the state?
If that is run again, then the definition of xIsNext and friends must be happening again?
"any insight"
.