Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React 中 Context 用法详解 #2

Open
beichensky opened this issue Feb 26, 2021 · 2 comments
Open

React 中 Context 用法详解 #2

beichensky opened this issue Feb 26, 2021 · 2 comments

Comments

@beichensky
Copy link
Owner

beichensky commented Feb 26, 2021

前言

本文已收录在 Github: https://github.com/beichensky/Blog 中,欢迎 Star!

Demo 地址

Context 的创建

API: const MyContext = React.createContext(initialValue)

  • initialValuecontext 初始值

  • 返回值

    • MyContext.Provider: 提供者,是一个 React 组件,使用 Provider 标签包裹后的组件,自身以及后代组件都可以访问到 MyContext 的值

    • MyContext.Consumer: 消费者,是 React 组件,使用 Consumer 包裹后,可以使用 render props 的方式渲染内容,获取到 MyContext 的值

import React from "react";

const defaultTheme = { color: "black" };

const ThemeContext = React.createContext(defaultTheme);

Context.Provider 的使用

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

使用一个 Provider 来将当前的 context 传递给以下的组件树,无论多深,任何组件都能读取这个值。

接受一个属性 value,子组件中获取到的 context 值就是 value

  • 单个 Provider 使用
function App() {
  return (
    <ThemeContext.Provider value={{ color: "blue" }}>
      <p>Hello World</p>
    </ThemeContext.Provider>
  );
}
  • 多个 Provider 使用
function App() {
  const [count, setCount] = useState(0);
  return (
    <ThemeContext.Provider value={{ color: "blue" }}>
      <CounterContext.Provider value={{ count, setCount }}>
        <p>Hello World</p>
      </CounterContext.Provider>
    </ThemeContext.Provider>
  );
}

动态 Context

Context.Provider 不仅可以设置 value,也可以动态的修改 value

value 值发生变化的时候,所有依赖改 Context 的子组件都会进行渲染

创建动态 CounterContext

const defaultTheme = { color: "black" };
const defaultCounter = {
  count: 1,
  setCount: () => {},
};

const ThemeContext = React.createContext(defaultTheme);

const CounterContext = React.createContext(defaultCounter);

将修改 value 的方法作为 value 的属性传递下去

function App() {
  const [count, setCount] = useState(0);
  return (
    <ThemeContext.Provider value={{ color: "blue" }}>
      <CounterContext.Provider value={{ count, setCount }}>
        <p>App 页面 count: {count}</p>
        <ContextType />
        <HookContext />
        <ConsumerContext />
      </CounterContext.Provider>
    </ThemeContext.Provider>
  );
}

在子组件中调用修改 value 的方法

export default function HookContext() {
  const { color } = useContext(ThemeContext);
  const { count, setCount } = useContext(CounterContext);
  return (
    <>
      <h2 style={{ color }}>useContext 使用</h2>
      <p>
        <div>HookContext 页面 count: {count}</div>
        <button onClick={() => setCount(count + 1)}>increment</button>
      </p>
    </>
  );
}

消费 Context

  • static contextType

  • Context.Consumer

  • useContext

Class.contextType

  • 在类组件中设置静态属性 contextType 为某个 Context

  • 在使用的时候通过 this.context 获取到 Context 的值

export default class ContextType extends Component {
  static contextType = ThemeContext;
  render() {
    const { color } = this.context;
    return <h2 style={{ color }}>ContextType 使用</h2>;
  }
}

Context.Consumer

Context.Consumer 是一个 React 组件可以订阅 context 的变更,既可以在函数组件中使用也可以在类组件中使用

这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。

传递给函数的 value 值等等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Providervalue 参数等同于传递给 createContext()defaultValue

  • 消费一个 Context
export default function ConsumerContext() {
  return (
    <ThemeContext.Consumer>
      {(theme) => {
        const { color } = theme;
        return <h2 style={{ color }}>Context.Consumer</h2>;
      }}
    </ThemeContext.Consumer>
  );
}
  • 消费多个 Context
export default function ConsumerContext() {
  return (
    <ThemeContext.Consumer>
      {(theme) => (
        <CounterContext.Consumer>
          {(context) => {
            const { color } = theme;
            const { count } = context;
            return (
              <>
                <h2 style={{ color }}>Context.Consumer</h2>
                <p>ConsumerContext 页面 count: {count}</p>
              </>
            );
          }}
        </CounterContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

useContext

通过 useContext 可以获取到 value 值,参数是对应的 Context

可以再一个组件中使用多次 useContext 获取多个 Context 对应的 value

export default function HookContext() {
  const { color } = useContext(ThemeContext);
  const { count, setCount } = useContext(CounterContext);
  return (
    <>
      <h2 style={{ color }}>useContext 使用</h2>
      <p>
        <div>HookContext 页面 count: {count}</div>
        <button onClick={() => setCount(count + 1)}>increment</button>
      </p>
    </>
  );
}

displayName

context 对象接受一个名为 displayNameproperty,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。

示例,下述组件在 DevTools 中将显示为 MyDisplayName

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

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

总结

  • 创建 Context: const MyContext = React.createContext(defaultValue)

  • 使用 Context.Provider 组件将子组件进行包裹,则无论子组件层级多深,都可以获取到对应的 value

  • 使用 Class.contextType 的方式获取 Context,可以在组件中通过 this.context 的方式获取到对应的 value

    • 只能在类组件中使用
    • 组件中只能使用一个 Context
  • 使用 Context.Consumer 组件消费 Context

    • 需要一个函数作为子元素,函数参数是距离最近的 Contextvalue 值,返回一个组件
    • 可以在类组件中使用也可以在函数组件中使用
    • 可以消费多个 Context
  • 使用 useContext 消费 Context

    • 只能在函数组件中使用
    • 可以通过调用多次 useContext 消费多个 Context
  • Context.Provider 中的 value 发生变化,则依赖当前 Context 的子组件都会发生进行更新

写在后面

如果有写的不对或不严谨的地方,欢迎大家能提出宝贵的意见,十分感谢。

如果喜欢或者有所帮助,欢迎 Star,对作者也是一种鼓励和支持。

@huangsiyuan2015
Copy link

总结的很棒,受益了,赞一个!

@hesiyuetian
Copy link

总结的很到位 大佬

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants