React中Context 的作用及原理

React 中的 Context 提供了一种在组件树中传递数据的方法,而无需手动通过每个层级的 props 传递。它特别适合于全局状态的管理,如用户认证信息、主题设置或语言选择等。

1. 作用

  • 避免"props drilling":当一些数据需要通过多层嵌套组件传递时,使用 Context 可以避免每一层都通过 props 传递这些数据。

  • 全局状态管理:Context 提供了一种轻量级的全局状态管理方案,可以在应用的任何地方访问和更新状态。

  • 提高代码可读性和维护性:通过减少不必要的 props 传递,使代码更加简洁和易于维护。

2. 基础使用

React 的 Context 包括两个主要部分:Provider 和 Consumer。

  1. 创建 Context
javascript 复制代码
import React, { createContext } from 'react';
const MyContext = createContext();
  1. Provider:用于在组件树中提供 Context 的值。
javascript 复制代码
import React, { useState } from 'react';
import MyContext from './MyContext';

const MyProvider = ({ children }) => {
  const [value, setValue] = useState('default value');

  return (
    <MyContext.Provider value={{ value, setValue }}>
      {children}
    </MyContext.Provider>
  );
};
  1. Consumer:用于在需要的地方消费 Context 的值。
javascript 复制代码
import React, { useContext } from 'react';
import MyContext from './MyContext';

const MyComponent = () => {
  const { value, setValue } = useContext(MyContext);

  return (
    <div>
      <p>Context Value: {value}</p>
      <button onClick={() => setValue('new value')}>Change Value</button>
    </div>
  );
};
  1. 使用 Provider 包装组件树。
javascript 复制代码
import React from 'react';
import ReactDOM from 'react-dom';
import MyProvider from './MyProvider';
import MyComponent from './MyComponent';

const App = () => (
  <MyProvider>
    <MyComponent />
  </MyProvider>
);

ReactDOM.render(<App />, document.getElementById('root'));

3. 原理分析

  1. 创建Context

在 React 中,createContext 函数用于创建一个 Context 对象。这个函数的实现位于 packages/react/src/ReactContext.js 文件中。

javascript 复制代码
import { createContext } from 'react';
const MyContext = createContext(defaultValue);
javascript 复制代码
// ReactContext.js
export function createContext(defaultValue) {
  const context = {
    $$typeof: REACT_CONTEXT_TYPE,
    _currentValue: defaultValue,
    _currentValue2: defaultValue,
    _threadCount: 0,
    Provider: null,
    Consumer: null,
  };

  context.Provider = {
    $$typeof: REACT_PROVIDER_TYPE,
    _context: context,
  };

  context.Consumer = context;

  return context;
}

createContext 返回的对象包含 Provider 和 Consumer 属性。Provider 用于提供值,而 Consumer 用于消费值。

  1. Provider 提供值

Provider 组件用于在组件树中提供 Context 的值。其实现涉及到 ReactFiberHooks,该文件位于 packages/react-reconciler/src/ReactFiberHooks.js。

javascript 复制代码
function updateContextProvider(current, workInProgress, renderLanes) {
  const providerType = workInProgress.type;
  const context = providerType._context;

  const newProps = workInProgress.pendingProps;
  const oldProps = workInProgress.memoizedProps;
  const newValue = newProps.value;

  pushProvider(workInProgress, newValue);

  return workInProgress.child;
}

updateContextProvider 函数用于更新 Provider 组件的值,并将新值推入 Context 中。pushProvider 函数会将新值存储在 Context 对象的 _currentValue 属性中。

  1. Consumer 消费值

Consumer 组件用于在组件树中消费 Context 的值。其实现涉及到 ReactFiberNewContext 文件,位于 packages/react-reconciler/src/ReactFiberNewContext.js。

javascript 复制代码
function readContext(context, observedBits) {
  const value = isPrimaryRenderer ? context._currentValue : context._currentValue2;
  return value;
}

readContext 函数用于读取 Context 的值。根据当前的渲染器,它会读取 _currentValue 或 _currentValue2 属性的值。

  1. useContext Hook

useContext 是 React 16.8 引入的 Hook,使得函数组件能够方便地使用 Context。其实现位于 packages/react-reconciler/src/ReactFiberHooks.js 文件中。

javascript 复制代码
function useContext(Context) {
  const dispatcher = resolveDispatcher();
  return dispatcher.useContext(Context);
}

useContext 函数调用 resolvedDispatcher 来获取当前的 Hook dispatcher,然后调用 dispatcher 的 useContext 方法。

javascript 复制代码
function readContext(Context, observedBits) {
  const value = Context._currentValue;
  return value;
}
  1. 订阅更新

当 Provider 的 value 发生变化时,React 会通过其内部机制通知所有订阅了该 Context 的 Consumer 组件,从而触发重新渲染。

javascript 复制代码
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
  // Schedule the update on the fiber and its alternate
  const root = markUpdateLaneFromFiberToRoot(fiber, lane);
  if (root === null) {
    warnAboutUpdateOnUnmountedFiberInDEV(fiber);
    return;
  }

  markRootUpdated(root, lane, eventTime);
  ensureRootIsScheduled(root, eventTime);
}

scheduleUpdateOnFiber 函数用于调度更新操作,当 Context 的值发生变化时,会调用此函数来通知相关组件进行重新渲染。

  1. 小结

通过以上源码分析,我们可以看到 React 中 Context 的实现涉及到多个核心模块和函数:

  • 创建 Context:createContext 函数用于创建 Context 对象,包括 Provider 和 Consumer。

  • 提供值:updateContextProvider 函数用于更新 Provider 的值,并将其存储在 Context 对象中。

  • 消费值:readContext 函数用于读取 Context 的值,useContext Hook 使函数组件能够方便地使用 Context。

  • 订阅更新:当 Context 的值变化时,React 通过 scheduleUpdateOnFiber 函数调度相关组件的重新渲染。

4. 注意事项

  • 性能优化:过度使用 Context 可能会导致性能问题,特别是在高频率更新的情况下。可以通过分割 Context 或使用其他状态管理工具来优化性能。

  • 层级结构:尽量在需要的最小组件树范围内使用 Provider,以避免不必要的重新渲染。

相关推荐
小白每天学一点2 小时前
微信小程序开发学习-8
javascript·css·微信小程序·小程序·html
by__csdn2 小时前
Electron入门:跨平台桌面开发指南
前端·javascript·vue.js·typescript·electron·html
星离~11 小时前
Vue响应式原理详解:从零实现一个迷你Vue
前端·javascript·vue.js
一只小阿乐11 小时前
react 中的判断显示
前端·javascript·vue.js·react.js·react
小沐°11 小时前
React-页码组件
前端·javascript·react.js
诸葛亮的芭蕉扇14 小时前
抓图巡检-底图支持绘制
开发语言·前端·javascript
木子李BLOG15 小时前
Element Plus
前端·javascript·vue.js