如何使用 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 本身也有性能开销。只有在确实面临性能瓶颈时才考虑这些优化手段。

相关推荐
Dragon Wu5 分钟前
前端 下载后端返回的二进制excel数据
前端·javascript·html5
北海几经夏11 分钟前
React响应式链路
前端·react.js
晴空雨39 分钟前
React Media 深度解析:从使用到 window.matchMedia API 详解
前端·react.js
一个有故事的男同学40 分钟前
React性能优化全景图:从问题发现到解决方案
前端
探码科技42 分钟前
2025年20+超实用技术文档工具清单推荐
前端
Juchecar1 小时前
Vue 3 推荐选择组合式 API 风格(附录与选项式的代码对比)
前端·vue.js
uncleTom6661 小时前
# 从零实现一个Vue 3通用建议选择器组件:设计思路与最佳实践
前端·vue.js
影i1 小时前
iOS WebView 异步跳转解决方案
前端
Nicholas681 小时前
flutter滚动视图之ScrollController源码解析(三)
前端
爪洼守门员1 小时前
安装electron报错的解决方法
前端·javascript·electron