有一个带有平方的板,其值依赖于数组,它通过useState
钩进行处理。每次点击都会使该值增加一,但不幸的是,它会将该值增加两(第一次点击除外)。
我的问题是:
(1)为什么会发生这种情况,(2)如何避免这种情况发生,并且一般来说(3)有更好的方法用钩子处理这样的数组。
let emptyBoard = Array.from({ length: parseInt(props.rows, 10) }, () =>
new Array(parseInt(props.columns, 10)).fill(0)
);
const [squaresValues, setSquaresValue] = useState(emptyBoard);
function onClick(id) {
const [rowIndex, cellIndex] = id;
console.log("the " + id + " square was clicked");
setSquaresValue(prevValues => {
let newBoard = [...prevValues];
console.log("before: " + newBoard[rowIndex][cellIndex]);
newBoard[rowIndex][cellIndex] = newBoard[rowIndex][cellIndex] + 1;
console.log("after: " + newBoard[rowIndex][cellIndex]);
return newBoard;
}
);
}
日志:
the 3,0 square was clicked
before: 0
after: 1
the 3,0 square was clicked
before: 1
after: 2
before: 2
after: 3
可以看出,从第二次点击起,每次点击都会将值提高两次。
答案 0 :(得分:2)
正如Udaya Prakash在上面的评论中提到的,它被两次调用以确保setState独立且幂等。因此,如果我理解正确,两次调用它不是一个错误,但是第二次更改您的值是。
以下是来自同一GitHub问题的Dan Abramov的评论:
预计setState更新程序将在开发中的严格模式下运行两次。这有助于确保代码不一次依赖它们运行(如果异步渲染异常终止并重新启动,则情况并非如此)。如果您的setState更新程序是纯函数(应该是纯函数),那么这应该不会影响应用程序的逻辑。
我们可以通过深度复制您的prevValues
来解决此问题,而不是使用传播算子进行浅表复制。您可能已经知道,有多种方法可以深度复制对象,我们暂时可以使用JSON.parse(JSON.stringify(...)
,您可以将其替换为your favorite kind from here
setSquaresValue(prevValues => {
let newBoard = JSON.parse(JSON.stringify(prevValues)); // <<< this
console.log("before: " + newBoard[rowIndex][cellIndex]);
newBoard[rowIndex][cellIndex] = newBoard[rowIndex][cellIndex] + 1;
console.log("after: " + newBoard[rowIndex][cellIndex]);
return newBoard;
});
如果您想玩的话,我已经在codesandbox here中对其进行了模拟。
答案 1 :(得分:2)
您仍在更改状态,如果您具有纯组件,则在更改时它们不会重新呈现。如果您具有纯组件,则使用JSON.parse进行完整状态复制是一个坏主意,因为所有内容都会重新呈现。
let newBoard = [...prevValues];
newBoard[rowIndex] = [...newBoard[rowIndex]];
newBoard[rowIndex][cellIndex] =
newBoard[rowIndex][cellIndex] + 1;