反应清理功能不清理状态

React cleanup function doesn't clean state

各位专家, 请解释一下,为什么在下面的代码中 属性 的状态不会在 useEffect 清理函数中被清理?

我的组件:

export default function TestComp() {

  let { id } = useParams();
  const [value, setValue] = useState(null);
  console.log('[TestComp] called...');

  const cleanup = () => {
    console.log('[TestComp] old value', value);
    setValue(null);
  };

  useEffect(() => {
    console.log('[TestComp] id changed: ', id);
    console.log('[TestComp] value in useEffect', value);
    setValue(id);
    return () => {
      cleanup();
    }
  }, [id]);

  return (<React.Fragment>Test id: {id}</React.Fragment>)
}

控制台输出:

[TestComp] called... TestComp.js:8
[TestComp] old value satellites:MTP TestComp.js:11
[TestComp] id changed:  satellites:MTP TestComp.js:16
[TestComp] value in useEffect satellites:FPGA TestComp.js:17
[TestComp] called... 2 TestComp.js:8
[TestComp] old value satellites:FPGA TestComp.js:11
[TestComp] id changed:  satellites:FNE TestComp.js:16
[TestComp] value in useEffect satellites:MTP TestComp.js:17
[TestComp] called... TestComp.js:8

我预计,当第二次调用 useEffect 时,value 将被清除并且为 null,但它仍然保持旧值:

value in useEffect satellites:MTP TestComp.js:17

提前致谢。

我认为这是事情发生的顺序问题。

useEffect 将 'capture' 值作为闭包的一部分在调用时作为闭包的一部分,即当调用 TestComp 函数时。当 id 更改时,React 将安排对清理函数的调用,然后调用 effect。但是 TestComp 在调用新效果函数之前不会再次调用。

您可以在日志中看到这一点,因为清理函数中的每个 old value 紧接着是效果函数中的 id changed

并且因为调用 TestComp 时的新效果函数 'captures' 值,它不会看到清理函数设置的值。

另请注意,至少从测试中我看到 React 抱怨 valuecleanup 不在 useEffect 的依赖项中。

您可能想添加另一个 useEffect,因为在当前情况下,cleanup 函数将 运行 仅在 卸载 时这对当前的逻辑来说毫无用处。

export default function TestComp() {
  let { id } = useParams();
  const [value, setValue] = useState(null);
  console.log('[TestComp] called...');

  useEffect(() => {
    console.log('[TestComp] id changed: ', id);
    console.log('[TestComp] value in useEffect', value);
    setValue(id);

    /* v Useless cleanup because the component unmounts anyway,
         the `value` state will be cleaned automatically.
    return () => {
      cleanup();
    }
    */
  }, [id]);

  // ^ Firstly, the `value` changed on id change
  // v Secondly, the `value` will be reset on `value` change
  useEffect(() => {
    console.log('[TestComp] old value', value);
    setValue(null);
  }, [value]);

  return <>Test id: {id}</>;
}
useEffect 中的

Return 函数只是在应用下一个效果之前清除之前的效果。但是您代码中的主要问题是

    const cleanup = () => {
    console.log('[TestComp] old value', value);
    setValue(null); // This is not prefer way to use setValue here.
}

通常,在清理期间我们取消订阅外部 service/subscription 但在这里您正在更改此处没有意义的状态并立即通过 useEffect setValue 获取更新 [= =47=] 并且在 setValue 内部清理调用之后,这也是再次调用 effect 的原因,

在你的 useEffect 中添加 setTimeout 后检查你的代码。

`useEffect(() => {
    console.log('[TestComp] id changed: ', id);
    console.log('[TestComp]:Effect value in useEffect', value);
    setValue(id);
    return () => {
      setTimeout(()=> cleanup(), 5000)
    }
  }, [id]);`

可能的解决方案-

  • 在上述情况下,您使用的是 id,将此 属性 提升到父组件并将其作为 属性 传递给 TestComp组件.

  • 当效果 运行 的整个组件重新渲染并且作用域被销毁但所有状态都保持在 useState 钩子的闭包内。

working example for above situation