组件跨层级通信-Context

457 阅读3分钟

什么是Context?

Context被翻译为上下文,在编程领域,这是一个经常会接触到的概念,React中也有。 在React的官方文档中,被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版 的App中使用Context。

为什么要使用Context?

当你的应用组件层级非常多的时候,有时候需要把props一层一层传递,尤其是把props从顶层传递到最底层,而中间的组件又不需要这个props的时候,React Context就显得尤其重要了。 image.png 如上图所示的情况下,需要将组件APP的值传递到最后一个组件Node,但是中间的组件又不需要这个props,这样就造成了没必要的渲染和性能的损耗。 React Context可以来处理这种混乱的场景,不需要逐层传递,达到按需消费的目的。

Context的使用方法(三步走源码

1、创建Context对象

Context.js

import React from "react";
export const Context = React.createContext();

2、创建生产者Provider,传递value

ContextPage.js

import React,{Component} from "react";
import { Context } from "../Contex";
import ContextTypePage from "./ContextTypePage";
export default class ContextPage extends Component {
  constructor(props){
    super(props);
    this.state = {
      theme:{
        themeColor:"red"
      }
    }
  }
  render(){
    const {theme} = this.state;
    return (
      <div>
        <Context.Provider value={theme}>
            <ContextTypePage/>
        </Context.Provider>
      </div>
    )
  }
}

3、子组件消费者使用value

3.1 第一种方式ContextType

ContextTypePage.js

import React,{Component} from "react";
import { Context,UserContext } from "../Contex";

export default class ContextTypePage extends Component {
  static contextType = Context;
  render(){
    const {themeColor} = this.context;
    return (
       <h3 className={themeColor}> 
          ContextTypePage
        </h3>
    )
  }
}

执行结果:

image.png

3.2 第二种方式useContext

3.2.1 创建useContext

export const UserContext = React.createContext();

3.2.2 创建生产者UserContext.Provider

<UserContext.Provider value={user}>
   <ContextTypePage/>
   <UseContextPage/>
</UserContext.Provider>

3.2.2 创建消费者UseContextPage

import React from "react";
import { Context,UserContext } from "../Contex";

export default function UseContextPage(props) {
    const user = React.useContext(UserContext);
    return ( 
        <div>
            <h3 className={theme.themeColor}>UseContextPage </h3>
        </div>
       
    );
}

执行结果:

image.png

3.3 第三种方式Consumer

3.3.1 创建消费者

import React,{Component} from "react";
import { Context,UserContext } from "../Contex";

export default class ConsumerPage extends Component{
    render(){
        return (
            <div>
                <h3>ConsumerPage</h3>
                <Context.Consumer>
                    {
                        theme => {
                            return (
                                <div className={theme.themeColor}>omg
                                <UserContext.Consumer>
                                    {
                                     user=><p>{user.name}</p>
                                    }
                                </UserContext.Consumer>
                                </div>
                            )
                        }
                    }
                </Context.Consumer>
            </div>
        )
    }
}

3.3.2 引入消费者

<Context.Provider value={theme}> 
   <UserContext.Provider value={user}>
      <ConsumerPage/>
   </UserContext.Provider>
</Context.Provider>

Context三种方法的比较

ContextType

挂载在 class 上的 contextType 属性会被重赋值为⼀个由React.createContext() 创建的 Context对象。这能让你使⽤ this.context 来消费最近 Context 上的那个值。你可以在任何⽣命周期中访问到它,包括 render 函数中。

  • 只能用在类组件中
  • 只能订阅单一的context来源,有多个的时候,后者会覆盖前者。

image.png

useContext

接收⼀个 context 对象( React.createContext 的返回值)并返回该 context 的当前值。当前的 context 值由上层组件中距离当前组件最近的 <MyContext.Provider> 的 value prop 决定。只能⽤ 在function组件中。

  • 只能在函数组件,自定义hook组件中使用
  • 可以订阅多个context来源

Consumer

React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context。 这个函数接收当前的 context 值,返回⼀个 React 节点。传递给函数的 value 值等同于往上组件树离 这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider, value 参数等同于传递 给 createContext() 的 defaultValue。

  • 不限制组件的类型

注意事项:

context 会使⽤参考标识(reference identity)来决定何时进⾏渲染,这⾥可能会有⼀些陷阱,当 provider 的⽗组件进⾏重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例⼦,当每⼀次 Provider 重渲染时,以下的代码会重渲染所有下⾯的 consumers 组件,因为 value 属性总是被赋值 为新的对象:

class App extends React.Component {
  render() {
    return (
      <Provider value={{ something: 'something' }}>
        <Toolbar />
      </Provider>
    );
  }
}

为了防⽌这种情况,将 value 状态提升到⽗节点的 state ⾥:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: { something: 'something' },
    };
  }
  render() {
    return (
      //FormPage.js
      class
        <Provider value = {this.state.value
  }>
    <Toolbar />
  </Provider >
  );
  }
 }

总结

在React的官⽅⽂档中, Context 被归类为⾼级部分(Advanced),属于React的⾼级API,建议不要滥 ⽤。