精读React hooks(五):useEffect使用细节知多少?
概念解释
当我们谈论 React,我们通常在讨论纯函数组件,这意味着它们没有副作用。但在实际应用中,我们通常需要执行有副作用的操作,如数据获取、手动更改 DOM、设置订阅等。useEffect
就是为此设计的。
useEffect的定义如下:
useEffect
接受两个参数:
- setup 函数:这是包含我们的 Effect 逻辑的函数。setup 函数里还可以选择返回一个
cleanup
函数,cleanup
函数会在组件卸载的时候执行。
- 依赖数组(optional dependencies):这是 setup 代码内部引用的所有响应式值的列表。响应式值包括 props、state 以及直接在组件主体中声明的所有变量和函数。依赖数组可以不传递、传空数组和非空数组。
用法详解
基础使用
最简单的用法,在useEffect
函数组件中执行副作用(例如:数据获取、手动修改 DOM、订阅事件等)。
不同的依赖数组的区别
现在升级上面这个示例,来看看不同依赖数组(optional dependencies)有什么区别
示例代码 TypeScript 版已发布到我的Github,演示Demo已发布到我的示例站
这个示例中,我们写了3个useEffect
,添加了不同的依赖数组,它们分别会有这样的表现:
- 第一个
useEffect
没有添加依赖数组,它的触发时机有:
- 组件挂载、卸载的时候
- 页面每一次
re-render
的时候,即leftCount
和rightCount
更新的时候,也是左按钮和右按钮点击的时候
- 第二个
useEffect
依赖数组添加了rightCount
,它的触发时机有:
- 组件挂载、卸载的时候
rightCount
触发的re-render的时候,即rightCount
更新的时候,也是右按钮点击的时候
- 第三个useEffect依赖数组说空的,它的触发时机有:
综上,我们只应该在必要的时候给useEffect
添加依赖项,这样可以避免一些不必要的重新渲染。
这个示例也说明了,在单个组件中允许使用多个useEffect
,我们可以按照不同的关注点将副作用逻辑分开。
清除函数的作用
开篇我们还提到一个cleanup
函数,cleanup函数只在组件卸载时执行,它的用法如下:
应用场景1:清除定时器
如果你在useEffect
中设置了一个定时器(如setTimeout
或setInterval
),在组件卸载前,你应该清除它,以防止它在组件不在 DOM 中时仍然执行。
应用场景2:事件监听器
为全局对象(如window
或document
)或特定 DOM 元素添加的事件监听器应当在组件卸载或不再需要它们时被移除。
应用场景3:WebSockets聊天室
当使用 WebSockets 或其他实时通信技术时,你可能会在组件加载时建立一个连接,而在组件卸载时需要断开这个连接。
应用场景4:请求的取消
对于可能在组件卸载后才完成的异步请求(如使用 Axios 发起的 HTTP 请求),你应该在组件卸载前取消它们,以防止设置已卸载组件的状态。
应用场景5:DOM 直接操作和第三方 DOM 库
如果使用了直接操作 DOM 的方法或使用了如 D3、animation 这样的第三方库来操作 DOM,你可能需要在组件卸载时清理或还原某些操作。
跳过初始渲染
在某些情况下,当组件首次渲染时,我们不希望立即执行某些操作。这些操作可能包括发送网络请求、触发某些动画或其他任务。而是只有在某个值或依赖项发生变化后,我们才希望执行这些任务。
假设我们有一个count
状态,我们希望当count
值发生变化时显示一个通知,但不希望在组件首次加载时显示这个通知。
这个示例中,当用户点击 Increase 按钮增加count
值时,会出现一个通知。但是在页面首次加载时,不会有任何通知出现。
结语
useEffect
是一个常用的 hook,正因为常用容易让开发者忽略了其中的使用细节,本文主要针对用法细节进行梳理,希望对大家有帮助。
专栏资源
专栏博客地址:精读React Hooks
专栏演示站:React Hooks Demos
专栏源码仓库:👉Github - Source Code
交个朋友:👉加入「独立全栈交流群」
专栏文章列表:
精读React hooks(一):useState 的几个基础用法和进阶技巧
精读React hooks(二):React状态管理的强大工具——useReducer
精读React hooks(三):useContext从基础应用到性能优化
精读React hooks(四):useRef的多维用途
精读React hooks(五):useEffect使用细节知多少?
精读React hooks(六):useLayoutEffect解决了什么问题?
精读React hooks(七):用useMemo来减少性能开销
精读React hooks(八):我们为什么需要useCallback
精读React hooks(九):使用useTransition进行非阻塞渲染
精读React hooks(十):使用useDeferredValue延迟状态更新
精读React hooks(十一):useInsertionEffect——CSS-in-JS样式注入新方式
精读React hooks(十二):使用useImperativeHandle能获得什么能力
精读React hooks(十三):使用useSyncExternalStore获取实时数据
精读React hooks(十四):总有一天你会需要useId为你生成唯一id
精读React hooks(十五):把useDebugValue加入你的React调试工具库
精读React hooks(十六):一个为代码优雅而生的hook——use