我正在通过使用TypeScript重构此tutorial中的代码来使用ReactJS中的新钩子功能。
我正在将回调从父组件传递到子组件,该子组件抛出了必须在单击按钮事件中执行的道具。
我的问题是:我有一个警报对话框,该对话框显示两次,而不是在赢得比赛时出现一次。
我认为这是由组件重新渲染引起的,因此我使用了useCallback
钩子来记住handleclickOnSquare
回调。事实是,警报对话框仍然出现两次。
我想我丢失了与重新渲染有关的东西,有人知道可能导致这种行为的原因吗?
这是我的代码:
Game.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,7],[2,5,];
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;
}
types.d.ts
type GameHistory = Array<{
squares: Array<string>
}>;
type HandleclickOnSquare = (index: number) => void;
谢谢