useEffect Hook 示例:导致重新渲染的原因是什么?

新手上路,请多包涵

我试图弄清楚 useEffect 何时导致重新渲染。我对以下示例的结果感到非常惊讶:

https://codesandbox.io/embed/romantic-sun-j5i4m

 function useCounter(arr = [1, 2, 3]) {
  const [counter, setCount] = useState(0);
  useEffect(() => {
    for (const i of arr) {
      setCount(i);
      console.log(counter);
    }
  }, [arr]);
}

function App() {
  useCounter();
  console.log("render");
  return <div className="App" />;
}

这个例子的结果如下:

在此处输入图像描述

我不知道为什么:

  1. 该组件仅渲染 3 次(我猜该组件会在每次调用 setCount + 一次初始渲染时重新渲染 - 所以 4 次)
  2. 计数器只有两个值 0 和 3:我猜,正如 本文 所述,每个渲染都会看到自己的状态和道具,因此整个循环将以每个状态作为常数 (1, 2, 3) 运行–>但是为什么状态永远不是 2?

原文由 Xen_mar 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.2k
2 个回答

我将尽我所能解释(或介绍)正在发生的事情。我也在第 7 点和第 10 点做了两个假设。

  1. 应用组件挂载。
  2. useEffect 挂载后调用。
  3. useEffect 将“保存”初始状态,因此 counter 在其中引用时将为 0。
  4. 循环运行 3 次。每次迭代 setCount 被调用来更新计数,控制台日志记录根据“存储”版本为 0 的计数器。因此数字 0 在控制台中记录了 3 次。因为状态已经改变 (0 -> 1, 1 -> 2, 2 -> 3) React 设置像一个标志或其他东西来告诉自己记住重新渲染。
  5. React 在 useEffect 执行期间没有重新渲染任何东西,而是等到 useEffect 完成重新渲染。
  6. 一旦 useEffect 完成,React 会记住 counter 的状态在其执行期间发生了变化,因此它将重新渲染应用程序。
  7. 应用程序重新渲染并再次调用 useCounter 。注意这里没有参数传递给 useCounter 自定义钩子。 假设: 我自己也不知道这一点,但我认为默认参数似乎是再次创建的,或者至少以某种方式让 React 认为它是新的。因此,因为 arr 被视为新的,所以 useEffect 挂钩将再次运行。这是我可以解释 useEffect 第二次运行的唯一原因。
  8. useEffect counter 的值将为 3。因此,控制台日志将按预期记录数字 3 三倍。
  9. useEffect 第二次运行后,React 发现计数器在执行期间发生了变化(3 -> 1, 1 -> 2, 2 -> 3),因此应用程序将重新渲染导致第三次“渲染”日志。
  10. 假设: 因为 useCounter 钩子的内部状态在此渲染和之前的渲染之间没有从应用程序的角度发生变化,它不会执行其中的代码,因此 useEffect 不叫第三次。所以应用程序的第一次渲染总是会运行钩子代码。第二次App看到hook的内部状态把它的 counter 从0变成了3,于是决定重新运行,第三次看到hook的内部状态还是3 3 所以它决定不重新运行它。这是我能想出的让钩子不再运行的最好理由。您可以在钩子本身中放置一个日志,以查看它实际上不会运行第三次。

这就是我看到的情况,我希望这让它更清楚一点。

原文由 ApplePearPerson 发布,翻译遵循 CC BY-SA 4.0 许可协议

我在反应文档中找到了第三次渲染的 解释。我认为这澄清了为什么在不应用效果的情况下进行第三次渲染的反应:

如果您将 State Hook 更新为与当前状态相同的值,React 将在不渲染子项或触发效果的情况下退出。 (React 使用 Object.is 比较算法。)

请注意,React 在退出之前可能仍需要再次渲染该特定组件。这不应该是一个问题,因为 React 不会不必要地“更深入”到树中。如果您在渲染时进行昂贵的计算,您可以使用 useMemo 对其进行优化。

似乎 useState 和 useReducer 共享这个救助逻辑。

原文由 Xen_mar 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进