解决react hooks依赖数组的一个方法

84 阅读1分钟

  使用useEffect、useCallback、useMemo等hook时,如果有依赖访问地址不定,依赖数组中添加相应依赖,否则就有闭包陷阱。一两个还好,多了就是一大串,useCallback还不如直接不用,useEffect、useMemo还要计算是哪个数据变化再计算,甚至要做防抖。解决这个问题的一个办法就是让这些函数访问数据的地址恒定,比如放到ref中。组件更新依旧会因为props、states的变化而更新,只需要保证它们变了,ref中的存储关系跟着变,为每个数据建一个地址恒定的getter,hooks被调用时使用getter就能访问到最新的数据。

  一个数据:

import { useCallback, useRef } from 'react';

export function useGetProp<T>(prop: T) {
    const ref = useRef<{ value: T }>({ value: prop });
    ref.current.value = prop;
    return useCallback(() => ref.current.value, []);
}

  多个数据:

import { useRef, useMemo } from 'react';
import { upperFirst } from 'lodash';

type CreateGetKey<T> = T extends `${infer F}${infer R}`
    ? `get${Uppercase<F>}${R}`
    : never;
type RecoverGetKey<T> = T extends `get${infer F}${infer R}`
    ? `${Lowercase<F>}${R}`
    : never;
type GetKeyMap<T, K extends keyof T = keyof T> = T extends Record<string, any>
    ? {
          [P in CreateGetKey<K>]: () => T[RecoverGetKey<P>];
      }
    : never;

export function useGetProps<T extends Record<string, any> = Record<string, any>>(
    data: T = {} as T,
) {
    const ref = useRef<T>(data);
    Object.keys(data).forEach((key: Exclude<keyof T, number | symbol>) => {
        ref.current[key] = data[key];
    });
    return useMemo(
        () =>
            Object.keys(data).reduce(
                (pre, key: Exclude<keyof T, number | symbol>) => {
                    pre[`get${upperFirst(key)}`] = () => ref.current[key];
                    return pre;
                },
                {} as GetKeyMap<T>,
            ),
        [],
    );
}
// 使用  

function Component({prop0, prop1,prop2}){
    const getProp = useGetProp(prop0);
    const { getProp1, getProp2 } = useGetProp({prop1,prop2});
    const onClick= useCallback((e: React.MouseEvent<HTMLElement, MouseEvent>)=>{
        const prop1= getProp1();
        // ...
    },[])
    // ...
}

  useGetProps能根据传入的变量名计算导出的getter名,而且有类型提示,但是这个类型也是有缺陷的,并不能反映实际生成的全部getter,比如:

    const {} = useGetProps({1: prop1})

此时类型认为getter名是数字,不会推导getter名。当然,可以在往ref中存和生成getter时只取符合类型要求的数据。