React---Context

74 阅读3分钟

context API 概念

Context 主要由三个部分组成:React.createContext 方法、Context.Provider 组件和 Context.Consumer 组件。

定义Context-React.createContext:

用于创建Context对象的方法。接受一个默认值作为参数,并返回一个包含Provider和Consumer组件的对象。默认值在组件树中找不到匹配的Provider时被使用。

// 创建一个名为 MyContext 的 Context 对象,并指定默认值为 "default value"
const MyContext = React.createContext("default value");

赋值Context-context.Provider组件:

Context.Provider 是用于提供数据的组件,它接收一个 value 属性作为数据的值。Provider 组件将 value 中的数据传递给其子组件中的所有 Consumer 组件。当 Providervalue 发生变化时,所有依赖该 ProviderConsumer 组件都会重新渲染。

import { MyContext } from './MyContext';

function App() {
  // 使用 MyContext.Provider 提供共享数据
  return (
    <MyContext.Provider value="Hello from Context">
      <ChildComponent />
    </MyContext.Provider>
  );
}

消费Context-Context.Consumer组件:

Context.Consumer 组件用于在组件树中消费共享的数据。它必须包含在 Context.Provider 内部,并且使用函数作为其子元素。这个函数接收当前的 Context 值作为参数,并返回 React 元素。当 Context.Providervalue 发生变化时,Context.Consumer 组件会重新渲染,获取最新的 Context 值。

Consumer 组件通过函数作为子组件的方式,将 Provider 提供的值作为该函数的参数,可以在函数体内使用该值。

import { MyContext } from './MyContext';
function ChildComponent() {
  // 使用 MyContext.Consumer 消费共享数据
  return (
    <MyContext.Consumer>
      {value => <div>{value}</div>}
    </MyContext.Consumer>
  );
}

Context使用场景

  • 深层嵌套:数据需要传递超过3-4层组件
  • 多处使用:多个不相关的组件需要同样的数据
  • 全局状态:主题、用户认证、语言设置等全局状态
  • 避免中间组件污染:中间件不应该知道传递的数据

Context不建议使用场景

  • 频繁变化的数据:可能导致大量重新渲染
  • 组件库开发:增加使用复杂度
  • 简单的父子通信:直接使用props更清晰

Context的性能问题

在大型应用中,React Context 可能会面临性能问题,主要涉及以下几个方面:

  • 嵌套层级过深:当 Context 嵌套层级过深时,每个 Consumer 组件在渲染时都需要检查其上层是否存在匹配的 Provider 组件。这种嵌套关系会增加渲染的开销,特别是在庞大的组件树中。
  • 未优化的更新触发:如果 Context 的值发生变化时,所有依赖该 ContextConsumer 组件都会触发重新渲染。在某些情况下,这可能会导致无关的组件重新渲染,造成性能浪费。

针对Context性能问题,可以采取以下优化策略:

    1. 避免过深的嵌套层级:尽量避免在组件树中创建过深的 Context 嵌套结构。可以通过重构组件结构、合并多个 Context,或者使用更细粒度的 Context 来减少嵌套层级。
    1. 使用 shouldComponentUpdate 或 React.memo:在 Consumer 组件中,可以使用 shouldComponentUpdateReact.memo 来进行性能优化。通过对比前后的 Context 值,可以避免不必要的重新渲染。
    1. 使用 useContext 和 useReducer:在性能要求较高的情况下,可以使用 useContext 配合 useReducer 来替代 useContext 配合 useState。因为 useReducer 提供了更细粒度的更新控制,可以减少不必要的重新渲染。
    1. 使用局部化的 Context:对于大型应用中的某些模块或功能,可以使用局部化的 Context,而不是将 Context 放置在整个应用的顶层。这样可以减少不必要的 Consumer 组件,提高渲染性能。
    1. 使用数据分离:对于只读的全局数据,可以考虑使用状态管理库(如 ReduxMobX)来管理,而不是使用 Context。这样可以提供更高效的状态管理和更新机制。

Context 原理

  • Provider 传递流程:Provider 的更新,会 深度遍历子代 fiber,消费 context 的 fiber 和父级链都会 提升更新优先级。 对于类组件的 fiber ,会 forceUpdate 处理。接下来所有消费的 fiber,都会 beginWork 。
  • Context 订阅流程: contextType , useContext, Consumer 会内部调用 readContext ,readContext 会把 fiber 上的 dependencies 属性 和 context 对象 建立起关联。