在 React 中如何使用 useMemo 和 useCallback 优化性能?

在 React 中,useMemouseCallback 是用于性能优化的 Hooks,它们通过缓存计算结果和函数引用来减少不必要的重渲染和计算。下面通过具体示例说明它们的使用场景和效果:

1. useMemo:缓存计算结果

当组件中有昂贵的计算操作时,useMemo 可以缓存计算结果,避免每次渲染都重新计算。

jsx 复制代码
import { useMemo, useState } from 'react';

function ExpensiveCalculation({ numbers }) {
  // 模拟一个昂贵的计算(如大量数据处理)
  const calculateSum = (nums) => {
    console.log("重新计算总和...");
    return nums.reduce((acc, num) => acc + num, 0);
  };
  
  // 不使用 useMemo:每次渲染都会重新计算
  // const total = calculateSum(numbers);
  
  // 使用 useMemo:只有 numbers 变化时才重新计算
  const total = useMemo(() => calculateSum(numbers), [numbers]);
  
  return <div>总和: {total}</div>;
}

function App() {
  const [count, setCount] = useState(0);
  const [numbers] = useState([1, 2, 3, 4, 5]);
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>计数: {count}</button>
      <ExpensiveCalculation numbers={numbers} />
    </div>
  );
}

效果 :点击按钮更新 count 时,ExpensiveCalculation 组件会重新渲染,但由于 numbers 没有变化,useMemo 会返回缓存的计算结果,避免了 calculateSum 函数的重复执行。

2. useCallback:缓存函数引用

当向子组件传递回调函数时,useCallback 可以缓存函数引用,防止因函数引用变化导致子组件不必要的重渲染。

jsx 复制代码
import { useCallback, useState, memo } from 'react';

// 使用 memo 包装子组件,仅在 props 变化时重渲染
const UserItem = memo(({ user, onDelete }) => {
  console.log(`UserItem ${user.id} 渲染`);
  return (
    <div>
      {user.name} 
      <button onClick={() => onDelete(user.id)}>删除</button>
    </div>
  );
});

function UserList() {
  const [users, setUsers] = useState([
    { id: 1, name: "Alice" },
    { id: 2, name: "Bob" }
  ]);
  const [filter, setFilter] = useState("");
  
  // 不使用 useCallback:每次渲染都会创建新的函数引用
  // const handleDelete = (id) => {
  //   setUsers(users.filter(user => user.id !== id));
  // };
  
  // 使用 useCallback:只有依赖项变化时才创建新函数
  const handleDelete = useCallback((id) => {
    setUsers(prevUsers => prevUsers.filter(user => user.id !== id));
  }, []); // 空依赖数组:函数引用始终不变
  
  return (
    <div>
      <input 
        placeholder="筛选" 
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
      />
      {users.map(user => (
        <UserItem 
          key={user.id} 
          user={user} 
          onDelete={handleDelete} 
        />
      ))}
    </div>
  );
}

效果 :输入筛选内容时,filter 状态变化会导致 UserList 重渲染。由于 handleDeleteuseCallback 缓存,其引用不会变化,因此 UserItem 组件不会不必要地重渲染。

关键区别与使用原则

特性 useMemo useCallback
作用 缓存计算结果 缓存函数引用
返回值 缓存的值 缓存的函数
典型场景 昂贵的计算操作 传递给子组件的回调函数

使用原则

  1. 不要过早优化:只有当确实存在性能问题时才使用它们
  2. 避免过度使用:缓存本身也有开销,适用于频繁重渲染的场景
  3. 正确设置依赖项:依赖数组必须包含所有函数内部使用的外部变量

总结

  • useMemo 解决的是重复计算的性能问题
  • useCallback 解决的是不必要的重渲染 问题(通常与 memo 配合使用)
  • 两者都是通过「缓存」来减少资源消耗,优化 React 应用的性能
相关推荐
顾安r1 小时前
11.8 脚本网页 星际逃生
c语言·前端·javascript·flask
Hello.Reader1 小时前
Data Sink定义、参数与可落地示例
java·前端·网络
im_AMBER2 小时前
React 17
前端·javascript·笔记·学习·react.js·前端框架
一雨方知深秋2 小时前
2.fs模块对计算机硬盘进行读写操作(Promise进行封装)
javascript·node.js·promise·v8·cpython
谷歌开发者3 小时前
Web 开发指向标 | Chrome 开发者工具学习资源 (六)
前端·chrome·学习
一晌小贪欢3 小时前
【Html模板】电商运营可视化大屏模板 Excel存储 + 一键导出(已上线-可预览)
前端·数据分析·html·excel·数据看板·电商大屏·大屏看板
发现你走远了3 小时前
连接模拟器网页进行h5的调试(使用Chrome远程调试(推荐)) 保姆级图文
前端·chrome
街尾杂货店&4 小时前
css - 实现三角形 div 容器,用css画一个三角形(提供示例源码)简单粗暴几行代码搞定!
前端·css
顺凡4 小时前
删一个却少俩:Antd Tag 多节点同时消失的原因
前端·javascript·面试
小白路过4 小时前
CSS transform矩阵变换全面解析
前端·css·矩阵