关于 useEffect 依赖数组的若干探究

1,959 阅读2分钟

zhuanlan.zhihu.com/p/528350799

随着 React hooks 越来越普遍,项目代码中经常充斥着大量带有依赖数组 deps 的 useEffect 钩子,因此对其执行时机的精准把握至关重要,于是对 useEffect 依赖数组的若干问题进行了详细探究和验证。

一、变化的判断方式

React 官方文档上说依赖数组“改变”才会触发执行,那么怎样才算改变?JS 中判断值相等常用三种,三等号、浅比较和深比较,首先应该排除深比较,因为 JS 对象非常灵活,定义深比较下的相等就不是一件容易的事,深层遍历还可能影响性能;鉴于在 React PureComponent 中用了浅比较,那依赖数组中使用浅比较也不是不可能,所以写个代码验证下:

import { useEffect, useRef, useState } from 'react';

function A ({a}) {
  useEffect(() => {
    console.log('changed: ', a.b)
  }, [a.b])

  // window.__ff = (i) => a.b = i; // 局部修改,不触发 useEffect

  return <h1>{JSON.stringify(a)}</h1>
}

function App() {
  const [t, st] = useState(0)

  const p = useRef({a: {b: {c: 0}}})

  window.__f = () => st(t => t + 1);

  p.current.a = {b: {c : t }}; // a 变化,触发 useEffect
  // p.current.a.b = {c : t};  // a.b 变化,触发 useEffect
  // p.current.a.b.c = t       // a.b.c 变化,不触发 useEffect


  return (
    <div className="App">
      <A a={p.current.a}/>
    </div>
  );
}

export default App;
  • a 变化时,在控制台下调用 __f(),输出 "changed" ;
  • a.b 变化时, 在控制台下调用 __f(),输出 "changed";
  • a.b.c 变化时, 在控制台下调用 __f(),不输出 "changed";

说明 依赖数组变化的判断方式是三等号。

二、变化是否一定执行

依赖数组变化后,传给 useEffect 的函数是否一定执行呢?答案是 否定的。从 文档

默认情况下,effect 会在每轮组件渲染完成后执行

的描述可以看出,函数执行的触发动作是渲染完成,而不是依赖数组改变;依赖数组改变只不是触发后的一个前置判断而已。

在上述代码中,取消注释 window.__ff 函数并在控制台下执行, 可以看到 不输出 "changed"。

三、理解 useEffect

useEffect 应该就如字面意思去理解:是 在组件每次渲染完成后、额外执行的一些操作(副作用);在实际中,有些操作不必每次渲染都执行,所以才通过依赖数组加以优化。

因此 依赖数组和响应式中的 watcher 有本质不同,不可混用。