首页 > 解决方案 > useEffect 依赖列表及其包含所有使用变量的最佳实践

问题描述

根据https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect在使用 useEffect 依赖数组时的反应文档,您应该传入效果内使用的所有值。

如果您使用此优化,请确保数组包含组件范围内的所有值(例如 props 和 state),这些值会随时间变化并由效果使用。否则,您的代码将引用以前渲染中的陈旧值。详细了解如何处理函数以及当数组值变化太频繁时该怎么做。

我不知道钩子在幕后是如何工作的,所以我在这里猜测一下。由于闭包中的变量可能会过时,这意味着该函数被缓存在某个地方。但是为什么要缓存该函数,因为除非依赖项更改并且无论如何都需要重新创建该函数,否则它不会被调用?

我做了一个小的测试组件。

function App() {
  const [a, setA] = useState(0);
  const [b, setB] = useState(0);

  useEffect(() => {
    console.log("b", b);
  }, [a]);

  return (
    <div>
      <div>App {a}</div>
      <button
        onClick={() => {
          setA(a + 1);
        }}
      >
        AddA
      </button>
      <button
        onClick={() => {
          setB(b + 1);
        }}
      >
        AddB
      </button>
    </div>
  );
}

你可以在这里尝试一下:https ://codesandbox.io/s/react-hooks-playground-forked-m5se8它工作得很好,没有陈旧的值。有人可以解释我错过了什么吗?

编辑:在反馈我的问题不完全清楚后,添加一个更具体的问题:

当页面加载后我单击 AddB 按钮然后单击 AddA 按钮时,控制台中显示的值为 1。根据文档,我应该得到一个过时的值 (0)。为什么不是这样?

标签: reactjsreact-hooksuse-effect

解决方案


当页面加载后我单击 AddB 按钮然后单击 AddA 按钮时,控制台中显示的值为 1。根据文档,我应该得到一个过时的值 (0)。为什么不是这样?

在这种情况下您看不到陈旧值的原因是当您单击时AddA会发生重新渲染。在该新渲染中,由于 的值a与以前的渲染不同,因此useEffect将安排在 react 更新 UI 后运行(但是它将引用的值将来自当前渲染useEffect- 因为每次传递给的函数都会重新创建)它的依赖项发生了变化,因此它从该渲染中捕获值)。

由于上述原因,这就是为什么您会看到b.

但是如果你有这样的代码

React.useEffect(() => {
    const timer = window.setInterval(() => {
       setB(b + 1);
    }, 1000);
    return () => {
      window.clearInterval(timer);
    };
  }, []);

b将是一个陈旧的关闭。由于useEffect没有重新运行,b总是从创建它的渲染中获得价值,即第一次渲染,上面的代码不会按预期工作,你可以自己检查。


我将从上面添加 Patrick Roberts 的此评论,因为它很好地解释了恕我直言,当他们说“否则,您的代码将引用以前渲染中的陈旧值”时反应文档可能意味着什么:

问题是您必须首先单击 AddA 按钮。在您单击 AddB 和 AddA 之间,效果会从陈旧的闭包中引用 b


推荐阅读