React Hooks之useCallback和useMemo

102 阅读3分钟

useCallback的基本使用:


const memoizedCallback = useCallback(
  () => {
    // 函数体
  },
  [dependency1, dependency2, ...] // 依赖数组
);

useCallback的本质工作不是在依赖不变的情况下组织函数创建,而是在依赖不变的情况下不返回新的函数地址而返回旧的函数地址。不论是否使用useCallback都无法阻止render时函数的重新创建。

useMemo

缓存数据

useMemo的核心目的是通过缓存计算结果,避免在组件渲染时进行不必要的重复计算,从而优化性能。这意味着只有当依赖项发生变化时,useMemo才会重新计算这个值,否则他将重用之前的结果,

const cachedValue = useMemo(calculateValue, dependencies)
  • calculateValue:这是一个用于计算我们想要缓存的值的函数。为了确保结果的稳定性和预测性,这个函数应该是一个纯函数。这意味着,它在相同的输入下总是返回相同的输出,并且没有任何副作用。
  • dependencies:这是一个数组,包含useMemo所依赖的变量或值。当数组中的任何值发生变化时,calculateValue函数将被重新执行。

缓存组件

useMemo的作用不只缓存数据,还可以缓存组件 如果你在渲染某个组件时有昂贵的计算,并且你想在某些依赖未改变时避免这些计算,那么也可以使用useMemo来缓存这个组件

function ParentComponent(props) {
    const [someData, setSomeData] = useState(initialData);

    const MemoizedExpensiveComponent = useMemo(() => {
        return <ExpensiveComponent data={someData} />;
    }, [someData]);

    return (
        <div>
            {MemoizedExpensiveComponent}
            {/* 其他组件和逻辑 */}
        </div>
    );
}

除了useMemo能够缓存组件,React还提供了memo这个高阶组件来完成相似工作 考虑到useMemomemo的特点,我们可以这么说:

  • 当你想避免因为数据变化而产生的不必要的计算时,使用useMemo
  • 当你想避免因为props 未变而产生的不必要的组件重渲染时,使用memo

useMemo和useCallback的差异

用途和缓存的内容不同:

  • useMemo: 用于缓存复杂函数的计算结果或者构造的值。它返回缓存的结果。
  • useCallback:用于缓存函数本身,确保函数的引用在依赖没有改变时保持稳定。

底层关联:

  • 从本质上说,useCallback(fn,deps)就是useMemo(() => fn, deps)的语法糖:
function useCallback(fn, dependencies){
    return useMemo(() => fn, dependencies)
}

什么时候使用useCallback

使用useCallback不意味着总是会带来性能提升;使用useCallback的场景:

  1. 子组件的性能优化: 当你将函数作为prop传递给已经通过React.memo进行优化的子组件时,使用useCallback可以确保子组件不会因为父组件中的函数重建而进行不必要的重新渲染。
  2. Hook依赖: 如果你正在传递的函数被用作其他Hook(例如useEffect)的依赖时,使用useCallback可以确保函数的稳定性,从而避免不必要的副作用的执行。
  3. 复杂计算与频繁的重新渲染:在应用设计很多细粒度的交互,如绘图应用或者其他需要大量操作和反馈的场景,使用useCallback可以避免因频繁的渲染而导致的性能问题。

避免使用useCallback

  1. 过度优化: 在大部分情况下,函数组件的重新渲染并不会带来明显的性能问题,过度使用useCallback可能会使代码变得复杂且难以维护
  2. 简单组件:对于没有经过React.memo优化的子组件或者哪些不会因为prop变化而重新渲染的组件,使用useCallback是不必要的
  3. 是代码复杂化;
  4. 不涉及其他Hooks的函数:如果一个函数并不被用作其他Hooks的依赖,并且也不被传递给任何子组件,那么没有理由使用useCallback