创见博客
useMemo以及useCallback的使用以及滥用后果
七崽爱吃小饼干2026/01/08阅读 0专栏 React

一、核心概念与适用场景

先简单理解这两个 Hook 的本质:

  • useMemo:缓存计算结果,避免组件每次渲染时重复执行昂贵的计算。
  • useCallback:缓存函数引用,避免组件每次渲染时创建新的函数实例。(可视为useMemo的语法糖)

1. 什么时候用 useMemo?

当你的组件中有昂贵的计算逻辑(比如复杂的数组遍历、数据转换、数学运算),且这些计算的依赖项没有变化时,使用 useMemo 缓存结果:

jsx
import { useMemo } from 'react';

function ExpensiveComponent({ list }) {
  // 场景1:昂贵的计算(比如过滤/排序大数组)
  const filteredList = useMemo(() => {
    // 假设 list 是长度上千的数组,过滤逻辑复杂
    return list.filter(item => item.value > 100 && item.status === 'active');
  }, [list]); // 仅当 list 变化时重新计算

  // 场景2:避免传递给子组件的复杂数据重复创建(导致子组件不必要渲染)
  const userInfo = useMemo(() => ({
    name: '张三',
    age: 20,
    address: '北京'
  }), []); // 依赖为空,仅初始化时创建一次

  return <Child data={filteredList} user={userInfo} />;
}

2. 什么时候用 useCallback?

当你需要把函数传递给子组件(尤其是使用 React.memo 包裹的纯子组件),或函数作为 useEffect/useMemo 的依赖时,使用 useCallback 缓存函数引用:

jsx
import { useCallback, useEffect, useState } from 'react';
import Child from './Child'; // 假设 Child 用 React.memo 包裹

function Parent() {
  const [count, setCount] = useState(0);

  // 场景1:传递给 memo 子组件的回调函数
  const handleClick = useCallback(() => {
    console.log('子组件点击');
  }, []); // 依赖为空,函数引用始终不变

  // 场景2:作为 useEffect 的依赖
  const fetchData = useCallback(() => {
    console.log('请求数据,count:', count);
  }, [count]); // 仅当 count 变化时更新函数引用

  useEffect(() => {
    fetchData();
  }, [fetchData]); // 依赖缓存后的函数,避免不必要的执行

  return <Child onClick={handleClick} />;
}

二、滥用的后果

useMemo 和 useCallback 并非“免费”,它们本身也有性能开销:

  1. 内存占用增加:缓存的计算结果/函数引用会一直存在于内存中,直到组件卸载或依赖项变化,大量不必要的缓存会占用更多内存。
  2. 初始化开销变大:每次渲染时,React 会先检查依赖项是否变化,再决定是否使用缓存,这个检查过程本身会消耗少量性能;如果是简单计算/简单函数,这个开销甚至会超过“重复执行/创建”的开销。
  3. 代码可读性降低:无意义地添加大量 useMemo/useCallback 会让代码变得冗余、难以维护,掩盖真正需要优化的点。

反例(滥用):

jsx
// 错误:简单计算无需 useMemo
const sum = useMemo(() => 1 + 1, []); 

// 错误:简单函数且不传递给子组件,无需 useCallback
const simpleFunc = useCallback(() => console.log('hello'), []);

三、判断是否需要使用的原则

  1. 先测后优化:先用 React DevTools 的 Profiler 工具定位性能瓶颈,确认是“重复计算”或“不必要的重渲染”导致的性能问题,再考虑使用。
  2. 聚焦“昂贵”操作:只有当计算/函数创建的成本 > 缓存的成本时,才值得使用。
  3. 子组件重渲染优化:仅当子组件用 React.memo/PureComponent 包裹,且传递的 props 是函数/复杂对象时,才需要用 useCallback/useMemo 缓存 props。

总结

  1. useMemo 用于缓存昂贵计算的结果,useCallback 用于缓存需要传递给子组件/作为依赖的函数引用。
  2. 滥用会增加内存占用、渲染时的检查开销,还会降低代码可读性。
  3. 优化的核心原则是“先定位瓶颈,再针对性优化”,避免无意义的缓存。
评论
0/100