preface

In the React application, data is passed from parent to child through the props property. When the number of component layers increases, it is cumbersome to pass props at each layer. Context provides a new way for components to share data, allowing data to be passed across generations. Without explicitly passing props layer by layer through the component tree.

According to the links on the official website, five common usage scenarios of Context are sorted out, and complete codes are accompanied with the article.

Usage Scenarios:

  • Parent component useProviderProduction data, used by sub-componentsConsumerConsumption data
  • Use of child componentsContextTypeReceive data
  • Dynamic and static contexts (the parent updates the Context, the children wrapped by the Provider refresh the data, and the children not wrapped by the Provider use the Context default)
  • Update Context in nested components (child components update data through functions passed by Context)
  • Consuming multiple contexts


Knowledge summary

  • React first creates a Context object. When React renders a component that subscribes to the Context object, the component will match the one closest to it in the component treeProviderRead the current context value.
  • Each Context returns a React component, allowing the consuming component to subscribe to changes in the Context
  • ContextType can only be used in class components
  • If a component has multiple consumers, one of the contextTypes will be valid, meaning only one ContextType will be availableProviderandConsumerForm)


Scenario 1: UseProviderandConsumerProduction and consumption data

File catalog and description

The following files are in the peer path:

  • Productcontext.js: created Context component file
  • Providerpage.js: Parent component (data producer)
  • Middlepage.js: Intermediate component
  • Consumerpage.js: Subcomponent (data consumer)

Data transfer path: providerPage.js ==> MiddlePage.js ==> ConsumerPage.js


Code files

  • ProductContext.js:
import React from 'react';

// Context allows us to pass values deep into the component tree without explicitly traversing each component.
// Create a Context object and give it a default value. If no match is found, the consuming component takes the default value of the Context
export const ProductContext = React.createContext({
  name: 'car'.price: 8000.unit: '$'});export const { Provider, Consumer } = ProductContext;
Copy the code
  • ProviderPage.js:
import React, { PureComponent } from 'react';
import MiddlePage from './MiddlePage';

import { Provider } from './ProductContext';


class ProviderPage extends PureComponent {

  state = {
    product: {
      name: 'plane'.price: 120000.unit: '$',}};render() {
    const { product } = this.state;

    return (
      <div>
        <h1>The root component uses the Provider to pass the value, and the child component uses the Consumer to receive it</h1>

        <Provider value={product}>
          <MiddlePage />
        </Provider>{/* Instead of Provider, display the Context object defaultValue*/} {/*<MiddlePage />* /}</div>); }}export default ProviderPage;
Copy the code
  • MiddlePage.js:
import React, { PureComponent } from 'react';
import ConsumerPage from './ConsumerPage';

class MiddlePage extends PureComponent {

  render() {
    return (
      <ConsumerPage />); }}export default MiddlePage;
Copy the code
  • ConsumerPage.js:
import React, { PureComponent } from 'react';
import { Consumer } from './ProductContext';


class ConsumerPage extends PureComponent {

  render() {
    return (
      <Consumer>
        {context => {
          return (
            <div>
              name:{context.name}
              <br />
              price:{context.price}
              <br />
              unit:{context.unit}
            </div>
          );
        }}
      </Consumer>); }}export default ConsumerPage;
Copy the code


The effect

You can see that the Context value provided by the ProviderPage component is displayed.


Scenario 2: UseContextTypeReceive data

File catalog and description

  • Familycontext.js: created Context component
  • Fatherpage.js: Parent component (production data)
  • Sonpage.js: Child component (intermediate component)
  • Grandsonpage.js: GrandsonComponent (consumption data)

Code files

  • FamilyContext.js
import React, { PureComponent } from 'react';
import SonPage from './SonPage';

import { Provider } from './FamilyContext';


class FatherPage extends PureComponent {

  state = {
    person: {
      name: 'Lora'.age: 99.gender: 'female',}};render() {
    const { person } = this.state;

    return (
      <div>
        <h1>Consume Context data using ContextType</h1>

        <Provider value={person}>
          <SonPage />
        </Provider>{/* Instead of Provider, display the Context object defaultValue*/} {/*<SonPage />* /}</div>); }}export default FatherPage;
Copy the code
  • Sonpage.js: same as middlepage.js above
import React, { PureComponent } from 'react';
import GrandSonPage from './GrandSonPage'

class SonPage extends PureComponent {


  render() {

    return (
      <GrandSonPage />); }}export default SonPage;
Copy the code
  • GrandSonPage. Js: useContextTypeReceive data
import React, { PureComponent } from 'react';

import { FamilyContext } from './FamilyContext';


class GrandSonPage extends PureComponent {

  //
  static contextType = FamilyContext;

  componentDidMount() {
    // Use contexType to access data in any lifecycle
    // Use this.context to consume the value of the most recent context
    const value = this.context;
    console.log(value);
  }

  render() {
    // Context is an object, and the object is destructed
    const { name, age, gender } = this.context;

    return (
      <div>
        name:{name}
        <br />
        age:{age}
        <br />
        gender:{gender}
      </div>); }}export default GrandSonPage;
Copy the code


The effect


Scenario 3: Dynamic and static contexts

In providerPage.js, provider-wrapped components can update the Context data, and non-provider-wrapped components can only get the Context default value.

Code files

  • ProductContext.js
import React from 'react';

// Context allows us to pass values deep into the component tree without explicitly traversing each component.
// Create a Context object
// Default value. If no Provider is matched, use the default value
export const ProductContext = React.createContext({
  name: 'car'.price: 17000.unit: '$'});export const { Provider, Consumer } = ProductContext;
Copy the code
  • ProviderPage.js
import React, { PureComponent } from 'react';
import { Button, Divider } from 'antd';
import MiddlePage from './MiddlePage';

import { Provider } from './ProductContext';


class ProviderPage extends PureComponent {

  state = {
    product: {
      name: 'plane'.price: 120000.unit: '$',}}; handleChange =() = > {
    const { product: { name, price, unit } } = this.state;

    this.setState({
      product: {
        name,
        price: price + 1,
        unit,
      },
    });
  };


  render() {
    const { product } = this.state;

    return (
      <div>
        <h1>The parent component updates the Context, the children wrapped by the Provider refresh the value, and the unwrapped children use the Context default value</h1>{/* Use the value of state in the internal component of the Provider package */}<Provider value={product}>
          <MiddlePage />
        </Provider>
        <Divider />{/* External components that are not in the Provider package use the default value of ProductContext again */}<MiddlePage />
        <Divider />
        <Button
          onClick={this.handleChange}
          type="primary"
        >increase</Button>
      </div>
      // {display the Context object defaultValue without Provider
      // <MiddlePage />); }}export default ProviderPage;
Copy the code
  • MiddlePage.js
import React, { PureComponent } from 'react';
import ConsumerPage from './ConsumerPage';

class MiddlePage extends PureComponent {

  render() {
    return (
      <ConsumerPage />); }}export default MiddlePage;
Copy the code
  • ConsumerPage.js
import React, { PureComponent } from 'react';
import { Consumer } from './ProductContext';


class ConsumerPage extends PureComponent {


  render() {

    return (
      <Consumer>
        {context => {
          return (
            <div>
              name:{context.name}
              <br />
              price:{context.price}
              <br />
              unit:{context.unit}
            </div>
          );
        }}
      </Consumer>); }}export default ConsumerPage;
Copy the code


The effect

Click the Add button, and the price above will be increased (using Context to update the value), while the price below will remain the same (using the Context default).


Scenario 4: Update the Context in a nested component

Code files

  • ProductContext.js
import React from 'react';

// Context allows us to pass values deep into the component tree without explicitly traversing each component.
// Create a Context object
// Notice that the first argument is an object {}
export const ProductContext = React.createContext({
    product: {
      name: 'car'.price: 8000.unit: '$',},// Pass a function through the context that causes the Consumer component to update the context
    handlePrice: () = >{},},);export const { Provider, Consumer } = ProductContext;
Copy the code
  • ProviderPage.js
import React, { PureComponent } from 'react';
import MiddlePage from './MiddlePage';

import { Provider } from './ProductContext';


class ProviderPage extends PureComponent {

  state = {
    product: {
      name: 'plane'.price: 120000.unit: '$',},// The state page contains the update function, so it is passed into the context provider
    handlePrice: () = > this.handlePrice(),
  };

  handlePrice = () = > {
    const { product: { name, price, unit } } = this.state;

    this.setState({
      product: {
        name,
        price: price + 1,
        unit,
      },
    });
  };


  render() {
    const { product, handlePrice } = this.state;

    return (
      <div>
        <h1>Updating the context via functions passed by the context is equivalent to updating the state in the child component</h1>{/* Notice that state is passed in, which contains the product object and a function */}<Provider value={{ product.handlePrice}} >
          <MiddlePage />
        </Provider>{/* Instead of Provider, display the Context object defaultValue*/} {/*<MiddlePage />* /}</div>); }}export default ProviderPage;
Copy the code
  • MiddlePage.js
import React, { PureComponent } from 'react';
import ConsumerPage from './ConsumerPage';

class MiddlePage extends PureComponent {

  render() {
    return (
      <ConsumerPage />); }}export default MiddlePage;
Copy the code
  • ConsumerPage.js
import React, { PureComponent } from 'react';
import { Button, Divider } from 'antd';
import { Consumer } from './ProductContext';


class ConsumerPage extends PureComponent {


  render() {
    return (
      <Consumer>The ConsumerPage component not only gets the value of the product object from the Context, Also get a handlePrice function * /} {/ * note and the Provider parameter name to incoming as well as defined in the context of the same * /} {the context = > {return (<div>
              name:{context.product.name}
              <br />
              price:{context.product.price}
              <br />
              unit:{context.product.unit}
              <Divider />
              <Button
                onClick={context.handlePrice}
                type="primary"
              >increase</Button>
            </div>
          );
        }}
      </Consumer>); }}export default ConsumerPage;
Copy the code


The effect

The add button is defined in the child component consumerPage.js, and the Context passes a function to the child. The specific function is implemented in the parent component providerPage.js, which feels like the React callback. The advantage is that the MiddlePage does not pass any props, including the callback function.


Scenario 5: Consuming multiple contexts

Code files

  • MultiContext.js
import React from 'react';


const SchoolContext = React.createContext({
  name: South Normal School Attached High School.location: 'nanjing'});const StudentContext = React.createContext({
  name: 'chengzhu'.age: 17});export { SchoolContext, StudentContext };
Copy the code
  • ProviderPage.js
import React, { PureComponent } from 'react';
import MiddlePage from './MiddlePage';

import { SchoolContext, StudentContext } from './MultiContext';


class ProviderPage extends PureComponent {

  state = {
    school: {
      name: 'Tsinghua University'.location: 'Beijing',},student: {
      name: 'zhang yun,.age: 22,}};render() {
    const { school, student } = this.state;

    return (
      <div>
        <h1>Consuming multiple contexts</h1>

        <SchoolContext.Provider value={school}>
          <StudentContext.Provider value={student}>
            <MiddlePage />
          </StudentContext.Provider>
        </SchoolContext.Provider>
      </div>

      // Display the default values defined in the Context without the Provider package
      // <MiddlePage />); }}export default ProviderPage;
Copy the code
  • MiddlePage.js
import React, { PureComponent } from 'react';
import ConsumerPage from './ConsumerPage';

class MiddlePage extends PureComponent {

  render() {
    return (
      <ConsumerPage />); }}export default MiddlePage;
Copy the code
  • ConsumerPage.js
import React, { PureComponent } from 'react';
import { SchoolContext, StudentContext } from './MultiContext';


class ConsumerPage extends PureComponent {


  render() {

    return (
      <SchoolContext.Consumer>
        {school => (
          <StudentContext.Consumer>
            {student => {
              return (
                <div>School: {school.name}<br />School location: {school. Location}<br />Student name: {student.name}<br />Student age: {student.age}</div>
              );
            }}
          </StudentContext.Consumer>
        )}
      </SchoolContext.Consumer>); }}export default ConsumerPage;
Copy the code


The effect

You can see that two contexts are passed, SchoolContext and StudentContext, and the child component ConsumerPage consumes the two contexts passed in.