全面理解useEffect——useEffect用法解析与使用技巧
全面理解useEffect
useEffect
可以说是react的hook中最强大的一个,他能够以一种极为优雅的方式模拟我们的生命周期。但是优雅的代价就是这个hook极其的晦涩,不容易被理解与掌握。下面我们就来全方位的探讨一下,useEffect
到底怎么用
useEffect的结构
在上一篇关于react的文章我提到过,函数式组件每次reRender
都会重新执行一遍,如果我们需要生命周期的效果,那我们就需要useEffect
函数。我们先来看一下useEffect的结构:
1 |
|
参数
setup
:一个实现了你需要的副作用逻辑的函数,这个函数将会在组件首次被绑定以及每次重新渲染导致dependencies
更新时调用。同时他还可以return
一个清除函数,这个函数将会在函数卸载以及每次重新渲染导致dependencies
更新之前调用dependencies
:在setup
中引用的所有reactive values
(state、参数以及在组件内部定义的变量和函数)或更新凭据组成的数组。这个数组必须是有限的并且偏平的,每次更新react将使用Object.is()
比较每个依赖项是否与之前相同,如果不同则更新。
return
undefinded
(无返回值)
中文博客中经常会漏掉setup
函数的返回值,但是它的返回值是这个函数的半壁江山,我们决不能将其忽略不提,如果忽略掉的话我们就有一半的效果无法实现了。
❗:useEffect
也是一个hook,请在组件或者自定义hook的最上层使用,不要在循环或分支结构中定义hook
模拟生命周期
react生命周期大概分为三种,mount
、update
、unmount
,当然在类式组件,我们已经可以规定是在mount之前,mount时还是mount之后调用,在函数式组件我们就不分的这么清楚了,因为在函数式组件我们的生命周期其实是独立的,是面向状态的。
下面我们就使用useEffect简单模拟一下类式组件的生命周期:
只在初始化时执行(DidMounted
) :
1 |
|
每次更新时执行(render
) :
1 |
|
每次卸载时执行(beforeUnmount
) :
1 |
|
实际上,我们经常说useEffect
的作用是模拟声明周期,而这似乎违背了函数式组件的初衷,也违背了useEffect
的初衷。我们应该多从状态的角度去思考,useEffect
并非是什么生命周期函数,而是状态更新函数。
控制更新
如果某个变量在每次组件更新的时候都需要更新,那我们完全没有必要使用useEffect
,我们使用副作用钩子一定是因为我们需要控制某个变量的更新。
我们可以使用useEffect
将组件内部的状态与组件外部系统同步:
1 |
|
或者我们可以使用useEffect
将组件组件外部系统与内部的状态同步:
1 |
|
其实生命周期也不过是对状态的统一更改,react现在只是将生命周期将颗粒度从组件降低到了状态。
一些tips
在useEffect中使用state
有时候我们可能会需要在useEffect
中使用和修改state
,但是这是极端危险的行为,因为当我们修改state
时会触发页面的rerender
,然后因为useEffect
依赖了state
,这将会导致state
被再次更改继而引起死循环……
1 |
|
如果我们在useEffect
中根据之前的state更新state,我们可以这么写:
1 |
|
如果你真的既要在Effect中读取state,又要修改state,那么我给出的建议是,:使用两个依赖同一个更新凭证useEffect
千万别这么做:
1 |
|
正确的做法如下:
1 |
|
消除不必要的对象与函数依赖
如果我们在组件内定义了一个函数或对象,我们在上一篇文章详细讲过,它每次都会被初始化。就像这样:
1 |
|
取而代之的是,如果你的对象或者函数只在useEffect
中使用,则写在setup
内部
1 |
|
如果你是希望有一个在整个组件都可以被访问的静态变量,那么可以将对象写在组件外部:
1 |
|
useLayoutEffect
我们在使用useEffect
的时候有时会出现一种问题:我们的组件会发生闪烁或突然的变化。
这时因为useEffect
是在mount
之后执行的,因此第一次渲染出的值是我们定义的初始值,如果更新的性能比较差或者更新的范围比较大,可能会导致我们的view
发生闪烁的现象。
这个时候我们就可以使用useLayoutEffect
,他和useEffect
的最大区别就是他是在mount
之前执行的,所以不会导致view
的闪烁。
但是如果我们没有很明显的ui闪烁问题,我们应该尽可能的使用useEffect
,因为它的运行效率更好。