Jansiel Notes

React 中 Context 用法详解

Context 的创建

API: const MyContext = React.createContext(initialValue)

  • initialValue:context 初始值
  • 返回值
    • MyContext.Provider: 提供者,是一个 React 组件,使用Provider标签包裹后的组件,自身以及后代组件都可以访问到MyContext的值
    • MyContext.Consumer: 消费者,是 React 组件,使用Consumer包裹后,可以使用render props的方式渲染内容,获取到 MyContext的值
1import React from \"react\";
2
3const defaultTheme = { color: \"black\" };
4
5const ThemeContext = React.createContext(defaultTheme);

Context.Provider 的使用

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。
使用一个 Provider 来将当前的 context 传递给以下的组件树,无论多深,任何组件都能读取这个值。
接受一个属性 value,子组件中获取到的 context 值就是 value 值

  • 单个 Provider 使用
1function App() {
2  return (
3    <ThemeContext.Provider value={{ color: \"blue\" }}>
4      <p>Hello World</p>
5    </ThemeContext.Provider>
6  );
7}
  • 多个 Provider 使用
 1function App() {
 2  const [count, setCount] = useState(0);
 3  return (
 4    <ThemeContext.Provider value={{ color: \"blue\" }}>
 5      <CounterContext.Provider value={{ count, setCount }}>
 6        <p>Hello World</p>
 7      </CounterContext.Provider>
 8    </ThemeContext.Provider>
 9  );
10}

动态 Context

Context.Provider 不仅可以设置 value,也可以动态的修改 value 当 value 值发生变化的时候,所有依赖改 Context 的子组件都会进行渲染

  • 创建动态 CounterContext
1const defaultTheme = { color: \"black\" };
2const defaultCounter = {
3  count: 1,
4  setCount: () => {},
5};
6
7const ThemeContext = React.createContext(defaultTheme);
8
9const CounterContext = React.createContext(defaultCounter);
  • 将修改 value 的方法作为 value 的属性传递下去
 1function App() {
 2  const [count, setCount] = useState(0);
 3  return (
 4    <ThemeContext.Provider value={{ color: \"blue\" }}>
 5      <CounterContext.Provider value={{ count, setCount }}>
 6        <p>App 页面 count: {count}</p>
 7        <ContextType />
 8        <HookContext />
 9        <ConsumerContext />
10      </CounterContext.Provider>
11    </ThemeContext.Provider>
12  );
13}
  • 在子组件中调用修改 value 的方法
 1export default function HookContext() {
 2  const { color } = useContext(ThemeContext);
 3  const { count, setCount } = useContext(CounterContext);
 4  return (
 5    <>
 6      <h2 style={{ color }}>useContext 使用</h2>
 7      <p>
 8        <div>HookContext 页面 count: {count}</div>
 9        <button onClick={() => setCount(count + 1)}>increment</button>
10      </p>
11    </>
12  );
13}

消费 Context 值

  • static contextType
  • Context.Consumer
  • useContext

Class.contextType

  • 在类组件中设置静态属性 contextType 为某个 Context
  • 在使用的时候通过 this.context 获取到 Context 的值
1export default class ContextType extends Component {
2  static contextType = ThemeContext;
3  render() {
4    const { color } = this.context;
5    return <h2 style={{ color }}>ContextType 使用</h2>;
6  }
7}

Context.Consumer

Context.Consumer 是一个 React 组件可以订阅 context 的变更,既可以在函数组件中使用也可以在类组件中使用
这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。
传递给函数的 value 值等等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue

  • 消费一个 Context
 1export default function ConsumerContext() {
 2  return (
 3    <ThemeContext.Consumer>
 4      {(theme) => {
 5        const { color } = theme;
 6        return <h2 style={{ color }}>Context.Consumer</h2>;
 7      }}
 8    </ThemeContext.Consumer>
 9  );
10}
  • 消费多个 Context
 1export default function ConsumerContext() {
 2  return (
 3    <ThemeContext.Consumer>
 4      {(theme) => (
 5        <CounterContext.Consumer>
 6          {(context) => {
 7            const { color } = theme;
 8            const { count } = context;
 9            return (
10              <>
11                <h2 style={{ color }}>Context.Consumer</h2>
12                <p>ConsumerContext 页面 count: {count}</p>
13              </>
14            );
15          }}
16        </CounterContext.Consumer>
17      )}
18    </ThemeContext.Consumer>
19  );
20}

useContext

通过 useContext 可以获取到 value 值,参数是对应的 Context
可以再一个组件中使用多次 useContext 获取多个 Context 对应的 value 值

 1export default function HookContext() {
 2  const { color } = useContext(ThemeContext);
 3  const { count, setCount } = useContext(CounterContext);
 4  return (
 5    <>
 6      <h2 style={{ color }}>useContext 使用</h2>
 7      <p>
 8        <div>HookContext 页面 count: {count}</div>
 9        <button onClick={() => setCount(count + 1)}>increment</button>
10      </p>
11    </>
12  );
13}

displayName

context 对象接受一个名为 displayName 的 property,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。
示例,下述组件在 DevTools 中将显示为 MyDisplayName:

1const MyContext = React.createContext(/* some value */);
2MyContext.displayName = \'MyDisplayName\';
3
4<MyContext.Provider> // \"MyDisplayName.Provider\" 在 DevTools 中
5<MyContext.Consumer> // \"MyDisplayName.Consumer\" 在 DevTools 中

总结

  • 创建 Context: const MyContext = React.createContext(defaultValue)
  • 使用 Context.Provider 组件将子组件进行包裹,则无论子组件层级多深,都可以获取到对应的 value 值
  • 使用 Class.contextType 的方式获取 Context,可以在组件中通过 this.context 的方式获取到对应的 value 值
    • 只能在类组件中使用
    • 组件中只能使用一个 Context
  • 使用 Context.Consumer 组件消费 Context
    • 需要一个函数作为子元素,函数参数是距离最近的 Context 的 value 值,返回一个组件
    • 可以在类组件中使用也可以在函数组件中使用
    • 可以消费多个 Context
  • 使用 useContext 消费 Context
    • 只能在函数组件中使用
    • 可以通过调用多次 useContext 消费多个 Context
  • Context.Provider 中的 value 发生变化,则依赖当前 Context 的子组件都会发生进行更新