【问题标题】:React 18 StrictMode first useEffect wrong stateReact 18 StrictMode 第一次使用Effect错误状态
【发布时间】:2022-11-28 19:30:14
【问题描述】:

另一个 React 18 严格模式问题。我知道 React 会调用渲染和效果函数两次,以突出显示即将推出的功能的潜在内存泄漏。我还不明白的是如何正确处理它。我的问题是我无法正确卸载第一个渲染结果,因为两个 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 调用,第二个渲染周期也没有为您提供第一个渲染周期的引用(它再次用 9 初始化,而不是 0.26890444169781214。还有useId 钩子返回两个不同的 ID,其中第二个 ID 也保留在进一步的渲染周期中。 这是错误还是预期的行为?如果是预期的,有没有办法解决这个问题?

【问题讨论】:

  • 它似乎在效果之前做了两个渲染周期,然后在不运行渲染周期的情况下进行效果清理,看起来 dev stritct 模式被破坏了 :P,它在生产中只运行一次,但对 dev stritct 模式的解释没有匹配这个输出

标签: javascript reactjs


【解决方案1】:

在 React 18 的 StrictMode 之前,您的组件只会挂载一次。但是现在,它们安装,卸载,然后重新安装。所以它不仅是运行两次的效果——你的整个组件被渲染了两次。

这意味着你的状态被重新初始化,你的引用也被重新初始化。显然,您的效果也会运行两倍。

关于运行两次的效果,您需要正确清理 async 效果 - 任何异步执行某些操作的效果,例如从服务器获取数据,添加事件侦听器等。并非所有效果都需要清理。

此外,这些效果意味着在开发中运行两次(它们在生产中只运行一次)。有些人试图阻止 effects 运行两次,但那是不行的。如果您正确地清理了一个效果,那么当它在生产中运行一次或在开发中运行两次时,它的执行应该没有区别。

此外,useId 挂钩返回两个不同的 ID,其中保留了第二个 ID 也在进一步的渲染周期中。这是错误还是预期的行为?如果 预计,有没有办法解决这个问题? 第二个值将是使用的值。这不是错误,您可以继续将其用作“真实”值。

您可以在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;
  }
}, [])

【讨论】:

  • 这只是重复网上写的内容,对我没有帮助。不管怎么说,多谢拉。我仍然有与渲染周期不匹配的效果。
  • 让我重新表述一下,当 React 现在正在挂载、卸载和重新挂载组件时。如何检测卸载?
  • 为此添加了一个 sn-p
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-06-13
  • 1970-01-01
相关资源
最近更新 更多