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 值变化时才会触发子组件的重新渲染,从而优化性能。

相关推荐
敲啊敲952710 分钟前
5.npm包
前端·npm·node.js
brrdg_sefg24 分钟前
Rust 在前端基建中的使用
前端·rust·状态模式
m0_748230941 小时前
Rust赋能前端: 纯血前端将 Table 导出 Excel
前端·rust·excel
qq_589568101 小时前
Echarts的高级使用,动画,交互api
前端·javascript·echarts
黑客老陈2 小时前
新手小白如何挖掘cnvd通用漏洞之存储xss漏洞(利用xss钓鱼)
运维·服务器·前端·网络·安全·web3·xss
正小安2 小时前
Vite系列课程 | 11. Vite 配置文件中 CSS 配置(Modules 模块化篇)
前端·vite
暴富的Tdy2 小时前
【CryptoJS库AES加密】
前端·javascript·vue.js
neeef_se2 小时前
Vue中使用a标签下载静态资源文件(比如excel、pdf等),纯前端操作
前端·vue.js·excel
m0_748235613 小时前
web 渗透学习指南——初学者防入狱篇
前端