我正在通过使用 TypeScript重构本教程中的代码来练习 ReactJS 中的新钩子功能。
我正在将一个回调从父组件传递给一个子组件,该子组件抛出了必须在单击按钮事件上执行的道具。
我的问题是:当游戏获胜时,我有一个警告对话框出现两次而不是一次。我认为这是由组件重新渲染引起的,所以我使用useCallback
钩子来记忆handleClickOnSquare
回调。问题是,警报对话框仍然出现两次。
我想我错过了与重新渲染有关的东西,有人知道可能导致这种行为的原因吗?
这是我的代码:
游戏.tsx
import React, { useState, useCallback } from 'react';
import './Game.css';
interface SquareProps {
onClickOnSquare: HandleClickOnSquare
value: string;
index: number;
}
const Square: React.FC<SquareProps> = (props) => {
return (
<button
className="square"
onClick={() => props.onClickOnSquare(props.index)}
>
{props.value}
</button>
);
};
interface BoardProps {
squares: Array<string>;
onClickOnSquare: HandleClickOnSquare
}
const Board: React.FC<BoardProps> = (props) => {
function renderSquare(i: number) {
return (
<Square
value={props.squares[i]}
onClickOnSquare={props.onClickOnSquare}
index={i}
/>);
}
return (
<div>
<div className="board-row">
{renderSquare(0)}
{renderSquare(1)}
{renderSquare(2)}
</div>
<div className="board-row">
{renderSquare(3)}
{renderSquare(4)}
{renderSquare(5)}
</div>
<div className="board-row">
{renderSquare(6)}
{renderSquare(7)}
{renderSquare(8)}
</div>
</div>
);
};
export const Game: React.FC = () => {
const [history, setHistory] = useState<GameHistory>(
[
{
squares: Array(9).fill(null),
}
]
);
const [stepNumber, setStepNumber] = useState(0);
const [xIsNext, setXIsNext] = useState(true);
const handleClickOnSquare = useCallback((index: number) => {
const tmpHistory = history.slice(0, stepNumber + 1);
const current = tmpHistory[tmpHistory.length - 1];
const squares = current.squares.slice();
// Ignore click if has won or square is already filled
if (calculateWinner(squares) || squares[index]) return;
squares[index] = xIsNext ? 'X' : 'O';
setHistory(tmpHistory.concat(
[{
squares: squares,
}]
));
setStepNumber(tmpHistory.length);
setXIsNext(!xIsNext);
}, [history, stepNumber, xIsNext]);
const jumpTo = useCallback((step: number) => {
setHistory(
history.slice(0, step + 1)
);
setStepNumber(step);
setXIsNext((step % 2) === 0);
}, [history]);
const current = history[stepNumber];
const winner = calculateWinner(current.squares);
const moves = history.map((step, move) => {
const desc = move ?
'Go back to move n°' + move :
'Go back to the start of the party';
return (
<li key={move}>
<button onClick={() => jumpTo(move)}>{desc}</button>
</li>
);
});
let status: string;
if (winner) {
status = winner + ' won !';
alert(status);
} else {
status = 'Next player: ' + (xIsNext ? 'X' : 'O');
}
return (
<div className="game">
<div className="game-board">
<Board
squares={current.squares}
onClickOnSquare={handleClickOnSquare}
/>
</div>
<div className="game-info">
<div>{status}</div>
<ol>{moves}</ol>
</div>
</div>
);
}
function calculateWinner(squares: Array<string>): string | null {
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;
}
类型.d.ts
type GameHistory = Array<{
squares: Array<string>
}>;
type HandleClickOnSquare = (index: number) => void;
谢谢