初学React Context

1,995 阅读4分钟

写了这么久的react,竟然这段时间才了解到这个context高阶api,可知我是多么无知且没有求知欲。从现在开始学习,永远都不会晚,加油哦

介绍

context译为‘上下文’,很多编程语言都有,react也有。
react常用context进行多级组件的数据传递。实现组件跨层级通信。

API

React.createContext

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

Context.Provider

Provider 接收⼀个 value 属性,传递给消费组件,允许消费 组件订阅 context 的变化。⼀个 Provider 可以和多个消费组 件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆 盖外层的数据。
当 Provider 的 value 值发⽣生变化时,它内部的所有消费组 件都会重新渲染。Provider 及其内部 consumer 组件都不受 制于 shouldComponentUpdate 函数,因此当 consumer 组 件在其祖先组件退出更新的情况下也能更新。

Context.Consumer

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

Class.contextType

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

你只通过该 API 订阅单⼀context。

使用

流程:创建context > 获取Provider和Consumer > Provider提供 值 > Consumer消费值

创建context

创建文件themeContext.js

import React from 'react'

// react中通过context实现祖组件向后台组件跨层级传值
// 创建一个context对象,创建一个上下文的容器(组件), defaultValue可以设置共享的默认数据
export const ThemeContext = React.createContext({themeColor: 'green'});

// Provider(生产者): 和他的名字一样。
// 用于生产共享数据的地方。生产什么呢? 那就看value定义的是什么了。value:放置共享的数据。
// <Provider value={/*共享的数据*/}>
    /*里面可以渲染对应的内容*/
// </Provider>
export const ThemeProvider = ThemeContext.Provider;

// Consumer(消费者):他是专门消费供应商(Provider 上面提到的)产生数据。
// <Consumer>
//  {value => /*根据上下文  进行渲染相应内容*/}
// </Consumer>
export const ThemeConsumer = ThemeContext.Consumer;

用Provider存入数据

import React, { Component } from 'react';
import { ThemeProvider, ThemeContext } from '../themeContext'; 
import ConsumerPage from './ConsumerPage';
import { Button } from 'antd';

class ContextPage extends Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: {
        themeColor: 'red'
      }
    };
  }

  changeColor = () => {
    this.setState({
      theme: this.state.theme.themeColor === 'red'? {themeColor: 'blue'} : {themeColor: 'red'}
    })
  }

  static contextType = ThemeContext;

  render() {
    console.log('context page: ', this.context.themeColor);  
    return (
      <ThemeProvider value={this.state.theme}>
        {/* 直接从context里取值,取到的都是默认值 */}
        <h3 style={{color: this.context.themeColor}}>
          ContextPage, color: {JSON.stringify(this.state.theme)}
        </h3>
        <Button onClick={this.changeColor}>change color</Button>
        <ConsumerPage/>
      </ThemeProvider>
    );
  }
}

export default ContextPage;

用Consumer接收数据

import React, { Component, Fragment } from "react";
import { ThemeConsumer } from "../themeContext";
import ConsumerPage1 from "./ConsumerPage1";

class ConsumerPage extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  render() {
    return (
      <ThemeConsumer>
        {value => (
          <div className="bordered">
            <h2 style={{ color: value.themeColor }}>Consumer Page</h2>
            <ConsumerPage1 />
          </div>
        )}
      </ThemeConsumer>
    );
  }
}

export default ConsumerPage;

注意事项

因为 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 (
            <Provider value={this.state.value}>
                <Toolbar />
            </Provider>
        );
    }
}

总结

在React的官⽅文档中, Context 被归类为高级部分 (Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。
不过,这并非意味着我们不需要关注 Context 。事实上,很 多优秀的React组件都通过Context来完成自己的功能,比如 react-redux的 ,就是通过 Context 提供一个 全局态的 store ,路由组件react-router通过 Context 管理路 由状态等等。在React组件开发中,如果用好 Context ,可以 让你的组件变得强大,而且灵活。


That's all. Thanks

脚踏实地,仰望星空。