如何使用 useMemo 或 useCallback 来避免 Context 使用中因父组件的渲染而导致子组件不必要的重复渲染

当你在 React 组件中使用 Context API 并把状态或计算得来的值传递给 Provider 时,如果这些值没有被正确的缓存,每次组件重新渲染都会创建一个新的对象或函数。这会导致所有消费该 Context 的组件不必要地重新渲染,即使传递的值在逻辑上并没有变化。为了避免这种情况,你可以使用 useMemouseCallback 钩子来优化性能。

使用 useMemo 缓存复杂计算的值

javascript 复制代码
import React, { useContext, createContext, useState, useMemo } from 'react';

const MyContext = createContext();

function ChildComponent() {
  const contextValue = useContext(MyContext);
  console.log('ChildComponent rendered!');
  return <div>{contextValue.complexValue}</div>;
}

function ParentComponent() {
  const [count, setCount] = useState(0);
  
  // 使用 useMemo 来缓存复杂计算的结果
  const contextValue = useMemo(() => {
    const complexValue = doComplexCalculation(count);
    return { complexValue };
  }, [count]);

  return (
    <MyContext.Provider value={contextValue}>
      <ChildComponent />
      <button onClick={() => setCount(c => c + 1)}>Increment</button>
    </MyContext.Provider>
  );
}

function doComplexCalculation(value) {
  // 假设这是一个复杂的计算
  console.log('Doing complex calculation...');
  return value * 2; // 简单示例
}

export default ParentComponent;

在这个示例中,即使 ParentComponent 组件因状态更新而重新渲染,contextValue 只有在 count 状态改变时才会重新计算。这避免了 ChildComponentcontextValue 引用变化而不必要的重新渲染。

使用 useCallback 缓存函数

javascript 复制代码
import React, { useContext, createContext, useState, useCallback } from 'react';

const MyContext = createContext();

function ChildComponent() {
  const { increment } = useContext(MyContext);
  console.log('ChildComponent rendered!');
  return <button onClick={increment}>Increment</button>;
}

function ParentComponent() {
  const [count, setCount] = useState(0);
  
  // 使用 useCallback 来缓存回调函数
  const increment = useCallback(() => {
    setCount(c => c + 1);
  }, []);

  const contextValue = useMemo(() => {
    return { count, increment };
  }, [count, increment]);

  return (
    <MyContext.Provider value={contextValue}>
      <div>Count: {count}</div>
      <ChildComponent />
    </MyContext.Provider>
  );
}

export default ParentComponent;

在这个示例中,increment 函数被 useCallback 缓存了下来,它的引用在渲染之间保持不变。这确保了当 ParentComponent 重新渲染时,ChildComponent 不会因为 increment 函数的引用改变而重新渲染。

这两个例子展示了如何通过 useMemouseCallback 来避免不必要的渲染。在大型应用中,尤其是当你有很多消费 Context 的组件时,使用这些优化可以显著提高性能。不过,也应该注意不要过度优化,因为 useMemouseCallback 本身也有性能开销。只有在确实面临性能瓶颈时才考虑这些优化手段。

相关推荐
夏幻灵1 分钟前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_15 分钟前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝18 分钟前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions27 分钟前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发27 分钟前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_34 分钟前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞0535 分钟前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、40 分钟前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao40 分钟前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架
杨超越luckly1 小时前
HTML应用指南:利用GET请求获取中国500强企业名单,揭秘企业增长、分化与转型的新常态
前端·数据库·html·可视化·中国500强