useEffect

264 阅读2分钟

前言

首先,在绝大部分开发场景下,是不需要修改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 useEffect fires after layout and paint, during a deferred event 在浏览器执行完布局(layout)和绘制(paint)之后调用useEffect.官方文档所说的during a deferred event并不是特指,没有特别的含义,只是表达了不是立即执行。关于layout和paint,可以参考aerotwist.com/blog/the-an…

清除

有一些副作用是需要清除的。例如eventlistener/定时器/订阅外部数据源等。这种情况下,清除工作是非常重要的,可以防止引起内存泄露。

如何清除

在useEffect中返回一个函数,在这个返回的函数中清除。官网举例如下:

image.png

何时清除

React 会在组件卸载的时候执行清除