英文:
Re-rendering inside an event handler
问题
import { useState } from "react";
function Square({ value, onSquareClick, winning }) {
console.log(winning);
return (
<button
className={winning ? "newsquare" : "square"}
onClick={onSquareClick}
>
{value}
</button>
);
}
function Board({ xIsNext, squares, onPlay, winning, handleWinning }) {
let winner;
function handleClick(i) {
if (calculateWinner(squares) || squares[i]) {
return;
}
const nextSquares = squares.slice();
if (xIsNext) {
nextSquares[i] = "X";
} else {
nextSquares[i] = "O";
}
onPlay(nextSquares);
winner = calculateWinner(nextSquares);
console.log(squares + " "); // <----------------------------------here
if (winner) handleWinning(winner);
}
console.log(squares + " "); // <----------------------------------here
let status;
if (winner) {
status = "Winner: " + squares[winner[0]];
console.log(winner);
} else {
status = "Next player: " + (xIsNext ? "X" : "O");
}
return (
<>
<div className="status">{status}</div>
<div className="board-row">
<Square
value={squares[0]}
onSquareClick={() => handleClick(0)}
winning={winning[0]}
/>
<Square
value={squares[1]}
onSquareClick={() => handleClick(1)}
winning={winning[1]}
/>
<Square
value={squares[2]}
onSquareClick={() => handleClick(2)}
winning={winning[2]}
/>
</div>
<div className="board-row">
<Square
value={squares[3]}
onSquareClick={() => handleClick(3)}
winning={winning[3]}
/>
<Square
value={squares[4]}
onSquareClick={() => handleClick(4)}
winning={winning[4]}
/>
<Square
value={squares[5]}
onSquareClick={() => handleClick(5)}
winning={winning[5]}
/>
</div>
<div className="board-row">
<Square
value={squares[6]}
onSquareClick={() => handleClick(6)}
winning={winning[6]}
/>
<Square
value={squares[7]}
onSquareClick={() => handleClick(7)}
winning={winning[7]}
/>
<Square
value={squares[8]}
onSquareClick={() => handleClick(8)}
winning={winning[8]}
/>
</div>
</>
);
}
export default function Game() {
const [history, setHistory] = useState([Array(9).fill(null)]);
const [currentMove, setCurrentMove] = useState(0);
const [winning, setWinning] = useState(Array(9).fill(false));
const xIsNext = currentMove % 2 === 0;
const currentSquares = history[currentMove];
function handlePlay(nextSquares) {
const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
setHistory(nextHistory);
setCurrentMove(nextHistory.length - 1);
}
function handleWinning(winningSquare) {
const newWinning = winning.slice();
newWinning[winningSquare[0]] = true;
newWinning[winningSquare[1]] = true;
newWinning[winningSquare[2]] = true;
setWinning(newWinning);
}
function jumpTo(nextMove) {
setCurrentMove(nextMove);
}
const moves = history.map((squares, move) => {
let description;
if (move > 0) {
description = "Go to move #" + move;
} else {
description = "Go to game start";
}
return (
<li key={move}>
<button onClick={() => jumpTo(move)}>{description}</button>
</li>
);
});
return (
<div className="game">
<div className="game-board">
<Board
xIsNext={xIsNext}
squares={currentSquares}
onPlay={handlePlay}
winning={winning}
handleWinning={handleWinning}
/>
</div>
<div className="game-info">
<ol>{moves}</ol>
</div>
</div>
);
}
function 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]) {
const winningSquares = lines[i].slice();
console.log(winningSquares);
return winningSquares;
}
}
return null;
}
我目前正在开发一个使用React.js的井字棋游戏应用程序,我在处理状态更新时遇到了一个控制台日志输出的问题。在阅读有关React状态更新和重新渲染行为的文章后,我预期两个console.log(squares)语句应该显示相同的输出。
我想了解这种行为背后的原因,以及如何确保两个console.log(squares)语句显示相同的输出。
任何见解或建议将不胜感激!谢谢!
英文:
import { useState } from "react";
function Square({ value, onSquareClick, winning }) {
console.log(winning);
return (
<button
className={winning ? "newsquare" : "square"}
onClick={onSquareClick}
>
{value}
</button>
);
}
function Board({ xIsNext, squares, onPlay, winning, handleWinning }) {
let winner;
function handleClick(i) {
if (calculateWinner(squares) || squares[i]) {
return;
}
const nextSquares = squares.slice();
if (xIsNext) {
nextSquares[i] = "X";
} else {
nextSquares[i] = "O";
}
onPlay(nextSquares);
winner = calculateWinner(nextSquares);
console.log(squares + " "); // <----------------------------------here
if (winner) handleWinning(winner);
}
console.log(squares + " "); // <----------------------------------here
let status;
if (winner) {
status = "Winner: " + squares[winner[0]];
console.log(winner);
} else {
status = "Next player: " + (xIsNext ? "X" : "O");
}
return (
<>
<div className="status">{status}</div>
<div className="board-row">
<Square
value={squares[0]}
onSquareClick={() => handleClick(0)}
winning={winning[0]}
/>
<Square
value={squares[1]}
onSquareClick={() => handleClick(1)}
winning={winning[1]}
/>
<Square
value={squares[2]}
onSquareClick={() => handleClick(2)}
winning={winning[2]}
/>
</div>
<div className="board-row">
<Square
value={squares[3]}
onSquareClick={() => handleClick(3)}
winning={winning[3]}
/>
<Square
value={squares[4]}
onSquareClick={() => handleClick(4)}
winning={winning[4]}
/>
<Square
value={squares[5]}
onSquareClick={() => handleClick(5)}
winning={winning[5]}
/>
</div>
<div className="board-row">
<Square
value={squares[6]}
onSquareClick={() => handleClick(6)}
winning={winning[6]}
/>
<Square
value={squares[7]}
onSquareClick={() => handleClick(7)}
winning={winning[7]}
/>
<Square
value={squares[8]}
onSquareClick={() => handleClick(8)}
winning={winning[8]}
/>
</div>
</>
);
}
export default function Game() {
const [history, setHistory] = useState([Array(9).fill(null)]);
const [currentMove, setCurrentMove] = useState(0);
const [winning, setWinning] = useState(Array(9).fill(false));
const xIsNext = currentMove % 2 === 0;
const currentSquares = history[currentMove];
function handlePlay(nextSquares) {
const nextHistory = [...history.slice(0, currentMove + 1), nextSquares];
setHistory(nextHistory);
setCurrentMove(nextHistory.length - 1);
}
function handleWinning(winningSquare) {
const newWinning = winning.slice();
newWinning[winningSquare[0]] = true;
newWinning[winningSquare[1]] = true;
newWinning[winningSquare[2]] = true;
setWinning(newWinning);
}
function jumpTo(nextMove) {
setCurrentMove(nextMove);
}
const moves = history.map((squares, move) => {
let description;
if (move > 0) {
description = "Go to move #" + move;
} else {
description = "Go to game start";
}
return (
<li key={move}>
<button onClick={() => jumpTo(move)}>{description}</button>
</li>
);
});
return (
<div className="game">
<div className="game-board">
<Board
xIsNext={xIsNext}
squares={currentSquares}
onPlay={handlePlay}
winning={winning}
handleWinning={handleWinning}
/>
</div>
<div className="game-info">
<ol>{moves}</ol>
</div>
</div>
);
}
function 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]) {
const winningSquares = lines[i].slice();
console.log(winningSquares);
return winningSquares;
}
}
return null;
}
I am currently working on a React.js application for a tic-tac-toe game, and I've encountered an issue with the console log output while handling state updates. After reading about React's state updates and re-rendering behavior, I expected both console.log(squares) statements to show the same output.
I would like to understand the reason behind this behavior and how I can ensure that both console.log(squares) statements show the same output.
Any insights or suggestions would be greatly appreciated! Thank you!
答案1
得分: 1
React更新以及状态变化在React中都是异步的。这意味着当你调用onPlay时,"squares"状态被上传,但是handleClick此时并不等待看状态是否实际被设置。所以你在更新之前就得到了输出。
如果你主要关心的是在状态更新时得到console.log,你可以使用useEffect钩子。
React.useEffect(() => {
console.log(squares);
}, [squares]);
你也可以将这个钩子用作一种回调,一旦状态发生变化就会触发。
参考:https://react.dev/reference/react/useEffect
英文:
React updates as well as the state changes are asynchronous with react. This means that when you call onPlay, the "squares" state is uploaded, but handleClick does not wait at this point to see if the state has actually been set. So you get the output before the update.
If your main concern is to get a console.log on status updates, you could use the useEffect hook
React.useEffect(() => {
console.log(squares);
}, [squares]);
you can also use this hook as a kind of callback as soon as a state has been changed
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论