我可以依赖组件中的 useEffect 顺序吗?

bel*_*a53 8 reactjs react-hooks

考虑以下模式:

const Comp = ({ handler })=> {
  // handler is some callback of the form ( ) => void
  useSomeHook(handler);
  //...
}

function useSomeHook(cb) {
  const ref = useRef()

  useEffect(() => {
    ref.current = cb; // update ref before second useEffect
  })

  useEffect(() => {
    // use the current (most recent) ref value
    const iv = setInterval(() => { ref.current() }, 3000) 
    return () => { clearInterval(iv) };
  }, []) // run only after first render
}
Run Code Online (Sandbox Code Playgroud)

问:我可以依靠的事实,首先useEffect始终执行之前第二个useEffect,所以ref已经更新?

背景:我的目的是避免记住Comp( useCallback)中传入的回调并将其设置cb为 dep。我特别感兴趣的是,如果这个可变引用模式在 React 世界中有效——上面只是一个人为的例子。

我想,在阅读了 Dan Abramov 的推文后,答案将是一个明确的“是的!” 。不过,以下问题指出:

我们不保证同级顺序,无论是在组件内还是同级之间。这是因为那里的强有力的保证阻碍了重构。在一个组件中,你应该能够重新排序 Hooks——尤其是自定义的

我是否将上述声明解释错了 - 或者链接有点矛盾?感谢任何澄清。

PS:我知道链接的帖子。这个问题更多地是关于一般模式、官方文档以及与提到的矛盾(?)陈述相关的。

谢谢!

Zac*_*ber 6

您调用它们的顺序很重要,据我所知,组件中的两个 useEffect 调用将始终以相同的顺序运行。这就是 React 如何识别哪个钩子是哪个(基于顺序)。

由于上述原因,您不能做的主要事情之一是有条件地运行钩子:React 跟踪根据它们在组件中调用的顺序(索引)调用的钩子。

这句话是在谈论组件中以及父组件和子组件之间的清理调用顺序。通常,您在清理部分所做的事情不应该影响其他钩子。通常你所做的是清除钩子中任何持久的副作用。诸如取消 API 调用和清除间隔之类的事情。

我们保证父效果在子效果之前被销毁。这样做的原因是父母往往倾向于依赖孩子创造的一些资源。例如从子节点强制管理的 DOM 节点中删除侦听器。如果孩子首先处置其资源,则父母可能无法正确清理自己。这不是 Hooks 特有的——它也是 componentWillUnmount 的工作方式。

我们不保证同级顺序,无论是在组件内还是同级之间。这是因为那里的强有力的保证阻碍了重构。在一个组件中,你应该能够重新排序 Hooks——尤其是自定义的。在兄弟姐妹之间,预计您也可以安全地重新订购它们。只有一个兄弟更新或卸载也是很常见的,因此兄弟之间的依赖关系无论如何都不可靠。

设置父子之间的顺序。例如,渲染 2 个子组件的父组件。

<Parent>
  <Child/>
  <Child/>
</Parent>
Run Code Online (Sandbox Code Playgroud)

编辑:这就是我重构您的代码的方式:

我会确保在初始 useRef 中包含 cb,然后在第一个效果的依赖数组中使用 cb 来更新 ref。

然后你一定要清除第二个效果中的间隔。

const Comp = ({ handler }) => {
  // handler is some callback of the form ( ) => void
  useSomeHook(handler);
  //...
};

function useSomeHook(cb) {
  const ref = useRef(cb);

  useEffect(() => {
    ref.current = cb; // update ref before second useEffect
  },[cb]);

  useEffect(() => {
    const id = setInterval(() => {
      ref.current();
    }, 3000); // use the current (most recent) ref value
    return () => {
      clearInterval(id);
    };
  }, []); // run only after first render
}
Run Code Online (Sandbox Code Playgroud)