WHAT - React Context 两层组件的优化机制

目录

一、Context

官方文档:https://react.dev/learn/passing-data-deeply-with-context

二、分离逻辑和视图

在 React 中使用两层组件来使用 Context 的原因主要是为了分离逻辑和视图,并且更好地管理和优化状态。

这种模式通常被称为"Context Provider/Consumer"模式。

以下是一些具体原因:

  1. 解耦状态逻辑和视图逻辑

    • Context Provider 组件通常负责管理和提供状态。它包含与状态管理相关的逻辑,例如获取数据、处理状态变化等。
    • Consumer 组件 负责渲染与状态相关的视图。它通过 Context 访问 Provider 提供的状态,从而保持视图逻辑的简洁和易维护。
  2. 提高组件的可复用性

    • 使用 Provider 和 Consumer 分离逻辑和视图后,可以更容易地在不同的视图组件中复用相同的状态逻辑,而无需重复编写相同的代码。
  3. 优化性能

    • Context 的更新会触发使用了这个 Context 的所有组件重新渲染。通过分层结构,可以在 Provider 中集中处理状态更新,减少不必要的重新渲染,提高性能。
    • Consumer 组件可以根据需要细粒度地订阅 Context 的某些部分,避免不必要的渲染。
  4. 简化测试

    • 逻辑和视图的分离使得单元测试更加简单。Provider 组件可以独立测试其状态管理逻辑,而 Consumer 组件可以通过模拟的 Context 值来测试其渲染逻辑。
  5. 增强代码的可维护性和可读性

    • 使用两层组件可以使代码结构更加清晰,职责分离明确。状态管理和 UI 逻辑分别处理,使得代码更容易理解和维护。

一个典型的使用示例如下:

jsx 复制代码
// 创建 Context
const MyContext = React.createContext();

// Provider 组件
const MyProvider = ({ children }) => {
  const [state, setState] = useState(initialState);

  const value = {
    state,
    updateState: (newState) => setState(newState),
  };

  return (
    <MyContext.Provider value={value}>
      {children}
    </MyContext.Provider>
  );
};

// Consumer 组件
const MyConsumer = () => {
  const { state, updateState } = useContext(MyContext);

  return (
    <div>
      <p>Current state: {state}</p>
      <button onClick={() => updateState("new state")}>Update State</button>
    </div>
  );
};

// 使用 Provider 包裹应用
const App = () => (
  <MyProvider>
    <MyConsumer />
  </MyProvider>
);

export default App;

通过这种方式,Provider 组件管理状态逻辑,Consumer 组件专注于渲染逻辑,从而实现了状态逻辑和视图逻辑的分离。

三、优化性能

  • Context 的更新会触发使用了这个 Context 的所有组件重新渲染。通过分层结构,可以在 Provider 中集中处理状态更新,减少不必要的重新渲染,提高性能。
  • Consumer 组件可以根据需要细粒度地订阅 Context 的某些部分,避免不必要的渲染。

这段话解释了 Context 的工作机制以及如何优化组件的渲染行为。具体来说,当 Context 的值发生变化时,所有使用 useContext 获取该 Context 的组件都会重新渲染,即使这些组件使用了 React.memoshouldComponentUpdate 来优化性能。这是因为 Context 的变化会绕过这些优化机制。

为了避免这种情况,可以将组件分为两个部分:一个外层组件从 Context 中读取所需的内容,并将这些内容作为 props 传递给使用 React.memo 优化的子组件。这样,只有当与子组件相关的 Context 值发生变化时,子组件才会重新渲染。

下面是一个示例代码和解释:

示例代码

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

// 创建 Context
const MyContext = createContext();

const MyProvider = ({ children }) => {
  const [state, setState] = useState({ count: 0, name: 'John' });

  return (
    <MyContext.Provider value={{ state, setState }}>
      {children}
    </MyContext.Provider>
  );
};

// 外层组件,从 Context 中读取所需内容
const OuterComponent = () => {
  const { state } = useContext(MyContext);
  
  // 只提取与子组件相关的值
  const name = state.name;

  return <InnerComponent name={name} />;
};

// 子组件,使用 memo 优化
const InnerComponent = memo(({ name }) => {
  console.log('InnerComponent render');
  return <div>Name: {name}</div>;
});

const App = () => (
  <MyProvider>
    <OuterComponent />
    <button onClick={() => setState((prev) => ({ ...prev, count: prev.count + 1 }))}>
      Increment Count
    </button>
  </MyProvider>
);

export default App;

解释

  1. 创建 Context :我们创建了一个 Context MyContext,并使用 MyProvider 组件来提供 Context 的值。

  2. MyProvider 组件MyProvider 组件管理状态 state,包含 countname,并将其提供给 Context。

  3. OuterComponent 组件OuterComponent 组件使用 useContext 钩子从 Context 中读取状态 state。然后,它提取出与子组件相关的 name 值,并将其作为 props 传递给 InnerComponent

  4. InnerComponent 组件InnerComponent 组件使用 React.memo 进行优化。由于它只接收 name 作为 props,因此只有在 name 发生变化时,它才会重新渲染。

  5. 避免不必要的重新渲染 :通过将 OuterComponent 作为中间层,从 Context 中读取值并将其传递给子组件,可以避免 InnerComponentcount 值变化时重新渲染,因为 InnerComponentcount 的变化不感兴趣。

这样,通过将组件分为两个部分,确保只有与子组件相关的 Context 值变化时才会触发子组件的重新渲染,从而优化性能。

相关推荐
毛骗导演2 分钟前
OpenClaw Auth Profile 与多 Key 冷却隔离机制深度解析:一个 API Key 是如何被选择、追踪并轮换的
前端·架构
用户9751470751362 分钟前
如何在 Vite 中配置 CSS 模块,以避免全局样式被模块化隔离覆盖?
前端
我叫黑大帅2 分钟前
Js常用的字符串处理
前端·javascript·面试
栀秋6663 分钟前
深入浅出:手写一个迷你版 Zustand
前端·react.js·前端框架
gustt3 分钟前
手写 Zustand:从零实现 React 轻量级状态管理库
前端·面试
读忆12 分钟前
在前端开发中使用组件后, 若是出了bug, 应该如何排查, 怎么排查, 解决方式是什么?
前端·javascript·vue.js·bug
We་ct17 分钟前
LeetCode 162. 寻找峰值:二分高效求解
前端·算法·leetcode·typescript·二分·暴力
HWL567919 分钟前
uni-app的生命周期
前端·vue.js·uni-app
softbangong20 分钟前
829-批量提取各子文件夹下文件到一级目录
java·服务器·前端·自动化工具·批量文件处理·文件提取工具·文件夹整理
李剑一21 分钟前
别再瞎写 Cesium 可视化!热力图 + 四色图源码全公开,项目直接复用!
前端·vue.js·cesium