前言
首先,在绝大部分开发场景下,是不需要修改react源码的,所以初级、中级工程师在理解其用法和运行机制前,可以先不研读源码,这只是一个优先级建议。
是什么
useEffect是react框架提供的hooks。那么要完全理解useEffect,先要搞清楚什么是hooks。 hooks其实省略了function,应该是hooks function即钩子函数,钩子可以理解为特殊的回调。所以useEffect可以认为是一个回调函数。下面从函数和回调两点来分析useEffect。
函数
作用: 可以让你在函数组件中执行副作用操作
参数: useEffect可以接收两个参数,
-
1.Accepts a function that contains imperative, possibly effectful code.
接收一个函数,这个函数包含命令式的代码,这些代码可能产生副作用。
即接收一个可能产生副作用的函数。
-
2.pass a second argument to useEffect that is the array of values that the effect depends on
接收一个数组,数组的元素决定副作用的执行时机。
- 如果不传,则表示依赖所有值,所以每次组件重新渲染,都会执行传入的函数;
- 如果传空数组,代表不依赖任何值,则只在组件加载时执行一次;
- 传入特定的值,则只在对应的值改变时才执行。
如何定义依赖改变
useEffect依赖改变是引用比较(浅比较/===),即指针改变视为改变。
如下场景clickObj函数无论执行多少次,都不会有console出现, 因为 a.aa.aaa 使用等于(===)上一次的 a.aa.aaa
const [obj, setObj] = useState({ a: { aa: { aaa: 1 } } })
useEffect(() => {
console.log(obj, '2222222222')
}, [obj.a.aa])
const onClickObj = () => {
setObj({ a: { aa: { aaa: 1 } } })
}
若把依赖改为obj,那么每次执行clickObj函数,都会有console出现, 因为obj === pre(obj)为false。每次set的都是新的指针。
关于引用比较和原值比较参考 juejin.cn/post/684490…
const [obj, setObj] = useState({ a: { aa: { aaa: 1 } } })
useEffect(() => {
console.log(obj, '2222222222')
}, [obj])
const onClickObj = () => {
setObj({ a: { aa: { aaa: 1 } } })
回调
- 1.谁来触发:
- react框架触发
- 2.触发时机
- the function passed to
useEffectfires after layout and paint, during a deferred event 在浏览器执行完布局(layout)和绘制(paint)之后调用useEffect.官方文档所说的during a deferred event并不是特指,没有特别的含义,只是表达了不是立即执行。关于layout和paint,可以参考aerotwist.com/blog/the-an…
- the function passed to
清除
有一些副作用是需要清除的。例如eventlistener/定时器/订阅外部数据源等。这种情况下,清除工作是非常重要的,可以防止引起内存泄露。
如何清除
在useEffect中返回一个函数,在这个返回的函数中清除。官网举例如下:
何时清除
React 会在组件卸载的时候执行清除