useEffect 完整指南(一)

977 阅读5分钟

你用hooks写了一些组件,或写了一个小型应用,你可能很满意,使用它的api很舒服,并且在这个过程中获得了一些小技巧,你甚至可能写了一些custom Hooks 去抽离重复的逻辑(精简掉300行代),并且得意的展示给你同事看

但有时候当你使用useEffect你总觉得哪里不对劲,你会嘀咕你可能遗漏了什么。他看起来像class的生命周期,但真的是这样子吗?你会发觉下面几个问题:

  • 如何用useEffect模拟componentDidMount生命周期呢?
  • 如何正确的在useEffect里请求数据?[]又是什么?
  • 我应该把函数当作effect的依赖吗?
  • 为什么有时候会出现无限重复请求的问题?
  • 为什么有时候在effect里拿到的是旧的stateprops

但我们开始使用hooks的时候,我也同样被上面的这些问题所困惑。在我经历了一下“啊哈!”的开窍时刻,我想把这些分享给你。这篇文章会深入讲解帮你说明上面问题的答案。

在看答案之前,我们需要先往后退一步。这篇文章的目的不是给你一个要点清单,而是想帮你真正领会useEffect,其实我们并没有太多需要学习的,事实上,我们会花很多时间试图忘掉已经学会的概念。

当我们不再透过class生命周期方法去窥探useEffect这个hooks的时候,我们才得以贯通

忘记你已经学会的

这篇文章假设你对useEffectApi有一定程度的了解

这篇文章很长,如果你很匆忙或者并不太关心本文主题的花,你也可以直接看下面的摘要。如果你对深入研究感觉不是很适应的花,你或许可以等下面这些解释出现在其他文章再去了解也行。就像2013年React刚问世的时候,大家需要时间去理解消化一种不同的心智模型,知识也需要时间去普及

摘要

Question:怎么用useEffect模拟componentDidMount生命周期?

虽然可以使用useEffect(fn,[]) ,但它们并不完全相等。和componentDidMount不一样,useEffect会捕捉propsstate (useEffect每一次渲染都有它自己的props and state,或新或旧) 了解更多可查看往期文章class和fn组件心智模型的区别

所以即便在回调函数里(useEffect中的fn),你拿到的还是初始化propsstate,如果你想要获取最新的值,你也使用ref ,不过通常会有更简单的实现方法,所以你并不一定要用ref

记住effect的心智模型和componentDidMount以及其他生命周期是不同的,试图找到它们之间完全一致的表达方式反而会更容易使你混淆,想要更有效,你需要思考effects,它的心智模型更接近于实现状态同步而不是响应生命周期时间(下文会详细介绍)

Question: 如何正确地在useEffect中请求数据?[]又是什么?

[] 表示effect没有使用任何react数据流的值,因此该effect仅被调用一次是安全的。

[] 同样也是一类常见问题的来源,你以为没使用数据流里的值但其实使用了,你需要学习一些策略( 主要是useReduceruseCallback)

Question: 我应该把函数当作effect的依赖吗?

一般建议把不依赖props和state的函数提到你的组件外面,并且把那些仅被effect使用的函数放在effect里面。

这样做了之后,你的effect还是需要用到组件内的函数(包括通过props传进去的函数),可以在定义它们的地方用useCallback包一层,为什么要这样做?应为这些函数可以访问到props和state,因此它们会参与到数据流中。官网FAQ有更详细的答案 (这段解释你可能听的雨里雾里不过没关系,会在下一篇文章讲解,回过头来你会恍然大悟的)

Question: 为什么有时候会出现无限重复请求的问题?

这个通常发生于你在effect做数据请求并且没有设置effect依赖项的时候。如果没有设置依赖,effect会在每次渲染后执行一次,然后在effect中更新状态引起渲染并从重新触发effect

无限循环的发生也可能是因为你设置的依赖总是被改变。你可以通过一个一个排除的方式来排查哪个依赖导致了问题,但是,排除你使用的依赖,或者盲目的使用[],通常是一个错误的解决方式,你应该做的是解决问题的根源。

举个例子,函数可能会导致这个问题,你可以把他们放在effect里,或者提到组件外面,或者使用useCallback包一层。useMemo做一些类似的事情以避免重新生成对象。

Question: 为什么有时候会在effect里拿到旧的state和props呢?

Effect拿到的总是定义它的那次渲染中的propsstate。这样能够避免一些bug,但在一些场景中又会有讨人嫌的时候。对于这种场景你可以明确的使用可变的ref保存一些值。

如果你觉得在渲染中拿到了一些旧的props和state,且不是你想要的,你可能遗漏了一些依赖,可以尝试使用这个 lint规则来训练你发现这些依赖的能力,可能没过几天这种能力变得像是你的第二天性,同样可以看官网FAQ的这个回答

篇幅所限,这篇只能讲一个摘要,下篇文章则会讲解react为什么会这样做?

下篇传送门