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

相关推荐
雪碧聊技术9 分钟前
深入解析Vue中v-model的双向绑定实现原理
前端·javascript·vue.js·v-model
快起来别睡了11 分钟前
手写 Ajax 与 Promise:从底层原理到实际应用
前端
打不着的大喇叭1 小时前
uniapp的光标跟随和打字机效果
前端·javascript·uni-app
无我Code1 小时前
2025----前端个人年中总结
前端·年终总结·创业
程序猿阿伟1 小时前
《前端路由重构:解锁多语言交互的底层逻辑》
前端·重构
Sun_light1 小时前
6个你必须掌握的「React Hooks」实用技巧✨
前端·javascript·react.js
爱学习的茄子1 小时前
深度解析JavaScript中的call方法实现:从原理到手写实现的完整指南
前端·javascript·面试
莫空00001 小时前
Vue组件通信方式详解
前端·面试
呆呆的心1 小时前
揭秘 CSS 伪元素:不用加标签也能玩转出花的界面技巧 ✨
前端·css·html
susnm1 小时前
Dioxus 与数据库协作
前端·rust