reactjs React 18 StrictMode首次使用效果状态错误

b09cbbtk  于 2022-11-29  发布在  React
关注(0)|答案(1)|浏览(213)

另一个React 18严格模式的问题。我知道React会两次调用render和effect函数,以突出即将推出的特性的潜在内存泄漏。我还不明白的是如何正确处理这个问题。我的问题是,我不能正确卸载第一个渲染结果,因为两次useEffect调用是在第二个渲染的状态下执行的。下面是一个示例来展示我的意思。

const ref = useRef(9);
  const id = useId();

  console.log('@@ initial id', id);
  console.log('@@ initial ref', ref.current);

  ref.current = Math.random();

  console.log('@@ random ref', ref.current);

  useEffect(() => {
    console.log('@@ effect id', id);
    console.log('@@ effect ref', ref.current);

    return () => {
      console.log('@@ unmount id', id);
      console.log('@@ unmount ref', ref.current);
    };
  });

下面是日志输出

@@ initial id :r0:
@@ initial ref 9
@@ random ref 0.26890444169781214
@@ initial id :r1:
@@ initial ref 9
@@ random ref 0.7330565878991766
@@ effect id :r1:                 <<--- first effect doesn't use data of first render cycle
@@ effect ref 0.7330565878991766
@@ unmount id :r1:
@@ unmount ref 0.7330565878991766
@@ effect id :r1:
@@ effect ref 0.7330565878991766

正如您所看到的,第一个渲染周期的状态没有useEffect调用,第二个渲染周期也没有为您提供第一个渲染周期的ref(它再次初始化为9,而不是0.26890444169781214)。此外,useId挂接返回两个不同的id,其中第二个Id也保留在后续的渲染周期中。这是一个错误还是预期行为?如果是预期行为,是否有方法修复?

gwbalxhn

gwbalxhn1#

在React 18的StrictMode之前,你的组件只会挂载一次。但是现在,它们会挂载,卸载,然后重新挂载。所以不仅仅是效果运行了两次--你的整个组件都渲染了两次。
这意味着你的状态被重新初始化,你的引用也被重新初始化。显然,你的效果也会运行两倍。
关于运行两次的效果,您需要正确清理async效果-任何异步执行某些操作的效果,如从服务器获取数据、添加事件侦听器等。并非所有效果都需要清理。
此外,特效在开发中应该运行两次(在生产中只运行一次)。有些人试图阻止特效运行两次,但这是不对的。如果你正确地清理了一个特效,它在生产中运行一次或在开发中运行两次应该没有区别。
此外,useId钩子返回两个不同的id,其中第二个Id也在以后的渲染周期中保留。这是一个bug还是预期的行为?如果是预期的,有什么方法可以解决这个问题吗?第二个值将是使用的值。它不是bug,你可以继续使用它作为“true”值。
您可以在StrictModehere上了解更多信息。
编辑:检测卸载。

// Create a ref to track unmount
const hasUnmounted = useRef(false)

useEffect(() => {
  return () => {
    // Set ref value to true in cleanup. This will run when the component is unmounted. If this is true, your component has unmounted OR the effect has run at least once
    hasUnmounted.current = true;
  }
}, [])

相关问题