在哪些情况下可以在没有回调的情况下更新状态导致问题?

时间:2021-01-04 22:40:34

标签: reactjs react-hooks react-state

考虑以下代码:

import React, { useState } from "react";

function App() {
  const [counter, setCounter] = useState(0);

  function handleClick() {
    setCounter(counter + 1); // update counter without using a callback
  }

  return (
    <div>
      <button type="button" onClick={() => handleClick()}>
        increase counter
      </button>
      <div>counter is {counter}</div>
    </div>
  );
}

export default App;

我是 React 的新手,听说如果一个状态应该基于它之前的值,我必须使用回调来更新状态。出于学习目的,我故意做了相反的事情:在不使用回调的情况下更新状态。我尽可能快地多次单击“增加计数器”按钮,但没有注意到任何问题。好像算对了。所以:

  • 我的示例代码是否会导致点击次数不计算在内,例如我点击按钮 10 次,但 counter 最终只有 9 次?
  • 这究竟是如何以及为什么会发生的?

1 个答案:

答案 0 :(得分:4)

考虑它的最好方法是您是否希望它围绕变量的当前值进行闭包。例如,玩这个:

function handleClick() {
  setCounter(counter + 1);
  setCounter(counter + 1);
}

您可能期望它是 2,但它是 1!这是因为两个调用都是setCounter(0 + 1)。但是,如果您这样做了:

function handleClick() {
  setCounter(c => c + 1);
  setCounter(c => c + 1);
}

你确实得到了 2。这是因为当每个 setCounter 函数运行时,它都会调用当前状态的回调(而不是它关闭的值)。

这是一个非常简单的例子,但现在考虑执行几个异步获取调用,如果不使用回调,可能会用另一个调用的状态覆盖一个调用!

const [data, setData] = useState({})

useEffect(() => {
  fetch("http://example.resource")
    .then(res => res.json())
    .then(res => {
      setData({...data, res})
    });

  fetch("http://another.example.resource")
    .then(res => res.json())
    .then(res => {
      setData({...data, res})
    });
}, []);

在本例中,无论哪个 fetch 第二次返回都会破坏第一次返回的数据,因为它将使用过时的 data 引用。