useState挂钩的“ setState”由一个调用发生两次

时间:2020-07-07 06:59:03

标签: arrays reactjs react-hooks use-state

有一个带有平方的板,其值依赖于数组,它通过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 

可以看出,从第二次点击起,每次点击都会将值提高两次。

2 个答案:

答案 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;