发布于 2026-01-05 5 阅读
0

当 useEffect 运行时

当 useEffect 运行时

useEffect是 React/Preact 中一个让人又爱又恨的钩子,但不管你喜不喜欢,了解它的工作原理总是有益的。这并非第一篇关于这个主题的博文,也肯定不会是最后一篇,但我希望能够解释一下它在应用程序中何时(以及为什么!)运行,供你参考!

请告诉我的朋友,这useEffect到底是什么?

你的朋友问了个好问题!首先,我们来谈谈应用程序中的副作用。我说的副作用是指当其他因素发生变化时所产生的影响。

例如,如果我有一个非常简单的add函数:

function add(x, y) {
    return x + y
}
Enter fullscreen mode Exit fullscreen mode

我可以模拟其他变量变化带来的副作用,就像这样:

let z = 10
function add(x, y) {
    z = z + x // this is the side effect, it does not change the return value
    return x + y
}
Enter fullscreen mode Exit fullscreen mode

在此处更改该变量并不会改变函数的返回值,这只是添加` !`z符号的副作用。xy

现在在 React/Preact 中,情况就复杂一些了,副作用并不总是好事。而useEffect`is` 通常是用来表示副作用的。开发者David Khourshid精辟地指出,它useEffect或许应该被命名为 `is` useSynchronize,因为它与其说是“状态改变时应该发生的额外事情”,不如说是“恰好与某些状态改变保持同步的事情”。

什么时候useEffect会被叫到?

所以,情况确实有点复杂,因为useEffect随着框架的更新,s 的行为也发生了一些变化,但总的来说:它会在组件挂载时调用,以及依赖数组中的任何内容发生变化时调用。我会更详细地解释这一点!

因此,我们以此为基础:

useEffect(() => {
  // your fetch call, changes, etc
  return () => {
    // clean-up
  }
}, [dependencyArray]) // we're staying in sync with this
Enter fullscreen mode Exit fullscreen mode

依赖关系数组

第二个参数useEffect称为依赖关系数组。这里可能发生三种情况:

  • 如果依赖数组为空,useEffect则只会调用一次(注意:由于 Suspense 的缘故,这在 React 18 中有所改变developmentstrict mode但 Preact 和 React 18 之前的版本就是这样,我将在本文后面讨论一种解决方法)。
  • 如果它不存在(例如完全省略),则会useEffect在每次状态改变时调用。
  • 如果它包含一个变量,那么useEffect当该变量发生变化时就会调用该函数。

如果该依赖关系数组已填充,您可以将该useEffect函数视为与数组中的变量保持“同步”。

返回函数

每当useEffect即将再次调用该函数,或者每当组件即将被卸载/销毁时,都会调用“清理函数”。

换句话说,当组件卸载时,或者当进行更新并且需要“取消”先前的效果时,React/Preact 会调用清理函数。

另一个更完整的例子:

useEffect(() => {
  let isCurrent = true
  fetchUser(uid).then((user) => {
    if (isCurrent) setUser(user)
  })
  return () => {
    isCurrent = false
  }
}, [uid])
Enter fullscreen mode Exit fullscreen mode

这看起来可能有点令人困惑,但它的工作原理是,当组件挂载时,组件会获取用户。

如果uid未发生更改且组件保持挂载状态,setUser则会调用该方法。如果uid在此期间发生更改,isCurrent则会将设置为 false false,因此setUser不会为该过期的 HTTP 调用调用该方法。

停止useEffect被召唤到山上

除了控制依赖数组变量之外,你可能还需要考虑的另一件事是:“嘿,我不想让这个效果在挂载时触发,而只想在依赖数组更新时触发。” 这很奇怪,但确实会发生。

对于这个特定情况,你需要引入useRef钩子。这里我就不解释这个钩子的作用了,因为这值得单独写一篇博文(Robin Wieruch这篇写得相当不错)。假设你有一个状态变量,你想让它保持同步:syncWithMe

const hasMounted = useRef(false);

useEffect(() => {
    if (hasMounted.current) {
        // code here only runs when syncWithMe changes!
    } else {
        hasMounted.current = true;
    }
}, [syncWithMe]);
Enter fullscreen mode Exit fullscreen mode

这叫做“ref 标志”!在这个例子中,hasMounted它充当一个实例变量,不会导致重新渲染或效果改变(因为它不是状态变量)。所以,你需要在true组件挂载时设置它,然后在它syncWithMe发生变化时,调用效果函数。

useEffect在 React 18+ 中,仅调用了 mount 事件

由于新的 Suspense 功能的工作方式以及 React 18 中的其他一些变化,useEffect需要进行更多调整才能使其只运行一次developmentstrict mode在生产环境中应该没问题,但还是值得讨论一下)。它看起来会很像我们之前的示例,但方向相反

const hasMounted = useRef(false);

useEffect(() => {
  if (hasMounted.current) { return; }

  // do something

  hasMounted.current = true;
}, []);
Enter fullscreen mode Exit fullscreen mode

useEffect如果我根本不想用呢?

那我觉得你或许应该看看或读一些关于为什么这样做不好的观点文章。呵呵。

useEffect这倒没什么不好,只是它需要根据场合和时间来选择,而且很多人对它的使用方式都有自己的看法。我建议大家看看这个演讲useEffect标题是“再见useEffect”(同样是David Khourshid的演讲,我上面也提到过他),讲解了一些关于何时应该使用以及何时不应该使用这种语言的细微差别。

希望这篇文章对您有所帮助!

文章来源:https://dev.to/cassidoo/when-useeffect-runs-3pf3