React Context 基本了解

611 阅读3分钟

概述

提供组件【 provider 组件 】: 提供给消费组件共享 value 值的父组件

消费组件【 consumer 组件 】: 共享提供组件 value 值的所有子组件

React.createContext【创建一个 Context 对象】

React 渲染一个订阅了这个 Context 对象的 consumer 组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context

const MyContext = React.createContext(defaultValue);

说明

  1. 当组件所属组件树中没有匹配Provider,设置的默认初始值 defaultValue 才会生效。
  2. 如果是将 undefined 传递给 Providervalue 时,默认初始值 defaultValue 不会生效。

Context.Provider【可以嵌套使用(内部覆盖外部)】

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化

<MyContext.Provider value={ 设置的公共组件需要的 context 的值 }>
  // 放置需要公用value值的组件
</MyContext.Provider>

说明

  1. value 属性值:提供给消费组件共享的数据值
  2. Providervalue 值发生变化时,它内部的所有消费组件都会重新渲染
  3. Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新

value 值为对象时,当 provider 父组件重新渲染时【value 会被赋予一个新的object】,可能会引起 consumer 子组件不必要的渲染

解决方法:可以将 value 值提升到 state 状态中管理】

Class.contextType【订阅 context】

// 方法1:
class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }

  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
}

MyClass.contextType = MyContext;


// 方法2: public class fields 语法【实验性语法】
class MyClass extends React.Component {
  static contextType = MyContext;
 
  render() {
    let value = this.context;
    /* 基于这个值进行渲染工作 */
  }
}

说明

  1. React.createContext() 创建的 Context 对象赋值给classcontextType 属性来订阅 context 内容
  2. 订阅 context 后可以在类组件中使用 this.context 来获取 Provider 组件中定义的 value 值,达到组件间的一些数据共享
  3. 可以在类组件的任何生命周期中访问 this.context
  4. 订阅多个 context 详情实例见官方文档

Context.Consumer【订阅 context 的变更, 更新 Context 对象中的值】

将共享的 context 设置为对象, 里面可以包含修改对应 context 值的方法

import { MyContext } from './theme-context';

function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数
  return (
    <MyContext.Consumer>
      {({theme, toggleTheme}) => (
        <button	onClick={toggleTheme}
          style={{backgroundColor: theme.background}}
        >
          Toggle Theme
        </button>
      )}
    </MyContext.Consumer>
  );
}

export default ThemeTogglerButton;

说明

  1. context 为父组件 MyContext.Provider 组件的 value 属性值【不需要在重新订阅了】
  2. customer 组件接受一个函数作为包裹内容,函数参数为对应的 provider 组件中设置的共享 value 值, 返回值为需要渲染的对应元素

Context.displayName 【定义 MyContext 在 devTool 中显示到名称】

通过给 Context 的属性 displayName 赋值来更改在开发工具审查元素中显示的对应元素名称

const MyContext = React.createContext(defaultValue);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider> //  在 DevTools 中显示为 "MyDisplayName.Provider"
<MyContext.Consumer> // 在 DevTools 中显示为 "MyDisplayName.Consumer"

函数组件中 Context 的使用

使用 useContext 获取 context 内容信息

// context.ts
const MyContext = React.createContext(defaultValue);

export default MyContext;

// App.tsx
const [state, setState] = useState({});

<MyContext.Provider value={state}>
  <MyComponent />
</MyContext.Provider>

// MyComponent.tsx
import React, { useContext } from 'react';
import MyContext from './context';

const MyComponent = () => {
  // 使用 state 展示值,使用 setState 更改值
  const { state, setState } = useContext(MyContext);

  return (
    /* 进行渲染工作 */
  )
}

export default MyComponent;

旧 Context API 类组件及函数组件使用

过时的 Context

// provider.tsx
class ContextProvider extends React.Component {
  constructor(props) {
    super(props);
    this.state = { value: {} }
  }

  // 当 state 或者 props 改变的时候,getChildContext 函数就会被调用
  getChildContext() {
    const { value } = this.state;
    return { value, changeValue };
  }

  // state 中 value 值改变函数
  changeValue = (value) => {
    this.setState({ value });
  }

  render() {
    return (
      <div>{this.props.children}</div>
    )
  }
}

ContextProvider.childContextTypes = {
  value: PropTypes.object
};

// App.tsx
<ContextProvider>
  <MyComponent />
</ContextProvider>

// 类子组件 MyComponent.tsx
class MyComponent extends React.Component {
  static contextTypes = {
    value: PropTypes.object
  };
  
  console.log(this.context); // { value: Object, changeValue: Function }
  
  render() {
    // 进行渲染工作
  }
}

// 函数子组件 MyComponent.tsx
const MyComponent = (props, context) => {

  console.log(this.context); // { value: Object, changeValue: Function }

  return (
    // 进行渲染工作
  )
}

MyComponent.contextTypes = {
  value: PropTypes.object
};