你用hooks
写了一些组件,或写了一个小型应用,你可能很满意,使用它的api
很舒服,并且在这个过程中获得了一些小技巧,你甚至可能写了一些custom Hooks 去抽离重复的逻辑(精简掉300行代),并且得意的展示给你同事看
但有时候当你使用useEffect
你总觉得哪里不对劲,你会嘀咕你可能遗漏了什么。他看起来像class
的生命周期,但真的是这样子吗?你会发觉下面几个问题:
- 如何用
useEffect
模拟componentDidMount
生命周期呢? - 如何正确的在
useEffect
里请求数据?[]
又是什么? - 我应该把函数当作
effect
的依赖吗? - 为什么有时候会出现
无限重复请求
的问题? - 为什么有时候在
effect
里拿到的是旧的state
或props
?
但我们开始使用hooks
的时候,我也同样被上面的这些问题所困惑。在我经历了一下“啊哈!”
的开窍时刻,我想把这些分享给你。这篇文章会深入讲解帮你说明上面问题的答案。
在看答案之前,我们需要先往后退一步。这篇文章的目的不是给你一个要点清单,而是想帮你真正领会useEffect
,其实我们并没有太多需要学习的,事实上,我们会花很多时间试图忘掉已经学会的概念。
当我们不再透过class
生命周期方法去窥探useEffect
这个hooks
的时候,我们才得以贯通
忘记你已经学会的
这篇文章假设你对useEffectApi有一定程度的了解
这篇文章很长,如果你很匆忙或者并不太关心本文主题的花,你也可以直接看下面的摘要。如果你对深入研究感觉不是很适应的花,你或许可以等下面这些解释出现在其他文章再去了解也行。就像2013年React刚问世的时候,大家需要时间去理解消化一种不同的心智模型,知识也需要时间去普及
摘要
Question:怎么用useEffect
模拟componentDidMount
生命周期?
虽然可以使用useEffect(fn,[])
,但它们并不完全相等。和componentDidMount
不一样,useEffect
会捕捉props
和state
(useEffect每一次渲染都有它自己的props and state,或新或旧) 了解更多可查看往期文章class和fn组件心智模型的区别
所以即便在回调函数里(useEffect中的fn)
,你拿到的还是初始化
的props
和state
,如果你想要获取最新
的值,你也使用ref
,不过通常会有更简单的实现方法,所以你并不一定要用ref
记住effect
的心智模型和componentDidMount
以及其他生命周期是不同的,试图找到它们之间完全一致的表达方式反而会更容易使你混淆,想要更有效,你需要思考effects
,它的心智模型更接近于实现状态同步,而不是响应生命周期时间
(下文会详细介绍)
Question: 如何正确地在useEffect中请求数据?[]又是什么?
[]
表示effect
没有使用任何react
数据流的值,因此该effect
仅被调用一次是安全的。
[]
同样也是一类常见问题的来源,你以为没使用数据流里的值但其实使用了,你需要学习一些策略( 主要是useReducer
和useCallback
)
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
拿到的总是定义它的那次渲染中的props
和state
。这样能够避免一些bug,但在一些场景中又会有讨人嫌的时候。对于这种场景你可以明确的使用可变的ref
保存一些值。
如果你觉得在渲染中拿到了一些旧的props和state,且不是你想要的,你可能遗漏了一些依赖,可以尝试使用这个 lint规则来训练你发现这些依赖的能力,可能没过几天这种能力变得像是你的第二天性,同样可以看官网FAQ的这个回答
篇幅所限,这篇只能讲一个摘要,下篇文章则会讲解
react
为什么会这样做?