【问题标题】:Can I rely on the useEffect order in a component?我可以依赖组件中的 useEffect 顺序吗?
【发布时间】:2020-07-22 02:44:33
【问题描述】:

考虑以下模式:

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
}

问题:我能否相信这样一个事实,即第一个 useEffect总是 执行 之前 第二个 useEffect,所以 ref 已经被执行了更新了吗?

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

我想,在阅读a tweet from Dan Abramov 之后,答案应该是明确的“是的!”。不过,following issue 表示:

我们不保证兄弟姐妹的顺序,无论是在组件内还是兄弟姐妹之间。这是因为那里的强保证阻碍了重构。 在组件内,您应该能够重新排序 Hook,尤其是自定义 Hook

我对上述陈述的解释是否错误 - 或者链接有点矛盾?感谢任何澄清。

PS:我知道linked post。这个问题更多地是关于一般模式、官方文档和提到的矛盾(?)陈述。

谢谢!

【问题讨论】:

标签: reactjs react-hooks


【解决方案1】:

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

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

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

我们保证在子效果之前销毁父效果。 原因是父母往往倾向于依赖一些 孩子创建的资源。比如从 DOM 中移除一个监听器 由孩子强制管理的节点。如果孩子将其 资源优先,父母可能无法正确清理自己 向上。这不是 Hooks 特有的——它是 componentWillUnmount 的方式 也可以。

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

父子之间的顺序已设置。例如,呈现 2 个 Children 的 Parent 组件。

<Parent>
  <Child/>
  <Child/>
</Parent>

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

我会确保在初始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
}

【讨论】:

  • 我相信您已经混淆了两个概念并因此误解了报价。是的,调用 useEffect() 钩子的顺序很重要(正如您所解释的,这是为了区分它们所必需的),但引用是说 回调传递给它们的顺序 不保证被调用。
  • 这句话特别提到了清理。我同意我错过了组件内不能保证清理的顺序,但这只是清理的顺序。 OP 询问他是否可以依赖在第二个之前运行的第一个 useEffect,这应该总是如此,否则钩子没有任何意义。
  • @ZacharyHaber 谢谢,你可能是对的,这个声明只是关于清理的(我不知道,comp 中的清理顺序是保证的)。我要求澄清这个问题 - 如果像你说的那样,我会接受你的回答
  • 据我了解,在 useEffect 中,清理函数(从回调返回)总是在下一个回调之前调用,因此清理可能出现乱序对 ( out-of) 下一个回调的排序。此外,实际上,如果您使用 useEffect 的第二个参数或 useEffect 中的任何异步函数,您将发生各种乱序......
猜你喜欢
  • 2017-06-07
  • 2016-04-07
  • 1970-01-01
  • 1970-01-01
  • 2021-08-20
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
  • 2011-01-23
相关资源
最近更新 更多