使用JSON.stringify()或 任何深度比较方法可能效率低下,如果您提前知道对象的形状,您可以编写自己的效果钩子,根据自定义相等函数的结果触发回调。
useEffect通过检查依赖项数组中的每个值是否与前一个渲染中的值相同,如果其中一个不是,则执行回调。因此,我们只需要保留我们有兴趣使用的数据实例,useRef并且仅在自定义相等检查返回false以触发效果时才分配一个新实例。
function arrayEqual(a1: any[], a2: any[]) {
  if (a1.length !== a2.length) return false;
  for (let i = 0; i < a1.length; i++) {
    if (a1[i] !== a2[i]) {
      return false;
    }
  }
  return true;
}
type MaybeCleanUpFn = void | (() => void);
function useNumberArrayEffect(cb: () => MaybeCleanUpFn, deps: number[]) {
  const ref = useRef<number[]>(deps);
  if (!arrayEqual(deps, ref.current)) {
    ref.current = deps;
  }
  useEffect(cb, [ref.current]);
}
用法
function Child({ arr }: { arr: number[] }) {
  useNumberArrayEffect(() => {
    console.log("run effect", JSON.stringify(arr));
  }, arr);
  return <pre>{JSON.stringify(arr)}</pre>;
}
更进一步,我们还可以通过创建一个接受自定义相等函数的效果钩子来重用钩子。
type MaybeCleanUpFn = void | (() => void);
type EqualityFn = (a: DependencyList, b: DependencyList) => boolean;
function useCustomEffect(
  cb: () => MaybeCleanUpFn,
  deps: DependencyList,
  equal?: EqualityFn
) {
  const ref = useRef<DependencyList>(deps);
  if (!equal || !equal(deps, ref.current)) {
    ref.current = deps;
  }
  useEffect(cb, [ref.current]);
}
用法
useCustomEffect(
  () => {
    console.log("run custom effect", JSON.stringify(arr));
  },
  [arr],
  (a, b) => arrayEqual(a[0], b[0])
);
现场演示
