beichensky / Blog

经常写博客的地方,会时常记录一些学习笔记、技术博客或者踩坑历程。
181 stars 12 forks source link

React 中 Context 用法详解 #2

Open beichensky opened 3 years ago

beichensky commented 3 years ago

前言

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

Demo 地址

Context 的创建

API: const MyContext = React.createContext(initialValue)

import React from "react";

const defaultTheme = { color: "black" };

const ThemeContext = React.createContext(defaultTheme);

Context.Provider 的使用

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

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

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

function App() {
  return (
    <ThemeContext.Provider value={{ color: "blue" }}>
      <p>Hello World</p>
    </ThemeContext.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

Class.contextType

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

export default function ConsumerContext() {
  return (
    <ThemeContext.Consumer>
      {(theme) => {
        const { color } = theme;
        return <h2 style={{ color }}>Context.Consumer</h2>;
      }}
    </ThemeContext.Consumer>
  );
}
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 中

总结

写在后面

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

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

huangsiyuan2015 commented 2 years ago

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

hesiyuetian commented 2 years ago

总结的很到位 大佬