React useEffect 的依赖值包含对象和数组时正确的用法

1,058 阅读2分钟

当依赖是基本数据类型的时候,因为是值比对,所以直接写就好了。但是如果依赖数据是对象或者数组。此时 useEffect 检测的是内存地址是否不同。而实际业务中,基本上也只要比对对象和数组中的值是否相同。正确的写法能减少组件刷新次数,从而优化性能

方法1、

使用 ahooks 中的 useDeepCompareEffect 进行处理

源代码

源码解析

  1. 思路:虽然调用 useEffect 的时候传递的依赖是数组或者对象,但是我们可以将其改造成基础数据类型
  2. 创建2个 ref 对象,一个用来记录上一次的依赖参数,另外一个 signalRef 设置成基础数据类型,用来触发 useEffect。使用 lodash 中的 isEqual 来检测2次依赖是否完全相同,不同的时候就将 signalRef 中的数字加1触发 useEffect 更新
  3. 简化后的代码如下
import { useRef } from 'react';
import isEqual from 'lodash/isEqual';

const depsEqual = (aDeps, bDeps) => {
  return isEqual(aDeps, bDeps);
};

export const createDeepCompareEffect = (hook) => (effect, deps) => {
  const ref = useRef();
  const signalRef = useRef(0);

  if (deps === undefined || !depsEqual(deps, ref.current)) {
    ref.current = deps;
    signalRef.current += 1;
  }

  hook(effect, [signalRef.current]);
};

方法2、

使用 react-use 中的 useDeepCompareEffect 进行处理

源代码

源码解析

  1. 创建一个 ref 来记录上一次的依赖参数,ref 初始值为 undefined
  2. 使用 fast-deep-equal/react 将这次的依赖和之前的做深度比对,如果不一致则修改 ref 的值,从而触发 useEffect 的更新

扩展知识点:fast-deep-equal 的使用

npm install fast-deep-equal

var equal = require('fast-deep-equal');
console.log(equal({foo: 'bar'}, {foo: 'bar'})); // true

//支持es6 map set 类型化素组
var equal = require('fast-deep-equal/es6');
console.log(equal(Int16Array([1, 2]), Int16Array([1, 2]))); // true

//搭配react使用
var equal = require('fast-deep-equal/react');
var equal = require('fast-deep-equal/es6/react');