Communication between React components

In the process of using React, it is unavoidable to need message transmission (communication) between components. There are several situations of communication between components in general:

  • Parent components communicate to child components
  • Subcomponents communicate to parent components
  • Communication between cross-level components
  • Communication between non-nested components

Below are several communication modes in turn.

Parent components communicate to child components

This is the simplest and most commonly used way of communication: the parent component passes props to the child component, and the child component gets props and processes it accordingly.

Here is the demo code:

The parent component App.js:

import React,{ Component } from "react";
import Sub from "./SubComponent.js";
import "./App.css";

export default class App extends Component{

    render(){
        return(
            <div>
                <Sub title = "No courtesy for this year's Festival" />
            </div>
        )
    }
}

SubComponent.js:

import React from "react";

const Sub = (props) => {
    return(
        <h1>
            { props.title }
        </h1>
    )
}

export default Sub;

Subcomponents communicate to parent components

The callback function can be used to communicate between the child component and the parent component: the parent component passes a function as props to the child component, and the child component calls the callback function to communicate with the parent component.

Here is the demo code:

SubComponent.js:

import React from "react";

const Sub = (props) => {
    const cb = (msg) => {
        return () => {
            props.callback(msg)
        }
    }
    return(
        <div>
            <button onClick = { cb("We communicate with each other.") }>Click on me</button>
        </div>
    )
}

export default Sub;

App.js:

import React,{ Component } from "react";
import Sub from "./SubComponent.js";
import "./App.css";

export default class App extends Component{
    callback(msg){
        console.log(msg);
    }
    render(){
        return(
            <div>
                <Sub callback = { this.callback.bind(this) } />
            </div>
        )
    }
}

Cross-level component communication

The so-called cross-level component communication means that the parent component communicates with the child component and the deeper child component. Cross-level component communication can take the following two ways:

  • Intermediate Component Layer Transfers Pros
  • Using context objects

For the first approach, if the parent component is deep in structure, then every layer of components in the middle will pass props, which increases the complexity, and these props are not what the intermediate components themselves need. However, this method is also feasible. When the component level is less than three layers, it can be used. When the component nesting is too deep, this method needs to be considered.

Using context is another feasible way. Context is equivalent to a global variable and is a large container. We can put the content we want to communicate in this container so that it can be freely accessed no matter how deep the nesting is.

Using context is also simple and requires two conditions:

  • The superior component declares that it supports context and provides a function to return the corresponding context object.
  • Subcomponents declare that they need to use context

Following is the code description, we create three new files: parent component App.js, child component Sub.js, and child component SubSub.js.

App.js:

import React, { Component } from 'react';
import PropTypes from "prop-types";
import Sub from "./Sub";
import "./App.css";

export default class App extends Component{
    // The parent component declares that it supports context
    static childContextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }

    // The parent component provides a function to return the corresponding context object
    getChildContext(){
        return{
            color:"red",
            callback:this.callback.bind(this)
        }
    }

    callback(msg){
        console.log(msg)
    }

    render(){
        return(
            <div>
                <Sub></Sub>
            </div>
        );
    }
} 

Sub.js:

import React from "react";
import SubSub from "./SubSub";

const Sub = (props) =>{
    return(
        <div>
            <SubSub />
        </div>
    );
}

export default Sub;

SubSub.js:

import React,{ Component } from "react";
import PropTypes from "prop-types";

export default class SubSub extends Component{
    // Subcomponents declare that they need to use context
    static contextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }
    render(){
        const style = { color:this.context.color }
        const cb = (msg) => {
            return () => {
                this.context.callback(msg);
            }
        }
        return(
            <div style = { style }>
                SUBSUB
                <button onClick = { cb("I Hu Han-san is back again!") }>Click on me</button>
            </div>
        );
    }
}

If the parent component communicates unidirectionally with the child component, variables can be used. If the child component wants to communicate with the parent component, the parent component can also provide a callback function for the child component to call back parameters.

There are two points to note when using context:

  • Parent components need to declare that they support context and provide ProTypes for attributes in context
  • Subcomponents need to declare that they need to use context and provide ProTypes with context attributes that they need to use
  • The parent component needs to provide a getChildContext function to return an initial context object

If you use a constructor in a component, you also need to pass in the second parameter context in the constructor and call the parent constructor in super to pass in context, otherwise you will not be able to use context in the component.

...
constructor(props,context){
  super(props,context);
}
...

Change the context object

We shouldn't and can't directly change the attributes in context objects. To change context objects, we only have to associate them with the state or props of the parent component. When the state or props of the parent component change, the getChildContext method is automatically called to return the new context object, and then the child component renders it accordingly.

Modify App.js to make context objects mutable:

import React, { Component } from 'react';
import PropTypes from "prop-types";
import Sub from "./Sub";
import "./App.css";

export default class App extends Component{
    constructor(props) {
        super(props);
        this.state = {
            color:"red"
        };
    }
    // The parent component declares that it supports context
    static childContextTypes = {
        color:PropTypes.string,
        callback:PropTypes.func,
    }

    // The parent component provides a function to return the corresponding context object
    getChildContext(){
        return{
            color:this.state.color,
            callback:this.callback.bind(this)
        }
    }

    // Modify the state of the parent component in this callback
    callback(color){
        this.setState({
            color,
        })
    }

    render(){
        return(
            <div>
                <Sub></Sub>
            </div>
        );
    }
} 

At this point, in the subcomponent's cb method, by passing in the corresponding color parameters, the context object can be changed, and then the subcomponent can be affected:

...
return(
    <div style = { style }>
        SUBSUB
        <button onClick = { cb("blue") }>Click on me</button>
    </div>
);
...

Context can also be used on stateless components, simply passing context as a second parameter:

import React,{ Component } from "react";
import PropTypes from "prop-types";

const SubSub = (props,context) => {
    const style = { color:context.color }
    const cb = (msg) => {
        return () => {
            context.callback(msg);
        }
    }

    return(
        <div style = { style }>
            SUBSUB
            <button onClick = { cb("I Hu Han-san is back again!") }>Click on me</button>
        </div>
    );
}

SubSub.contextTypes = {
    color:PropTypes.string,
    callback:PropTypes.func,
}

export default SubSub;

Communication between non-nested components

Non-nested components are components that do not have any relationships, including sibling components and non-sibling components that are not in the same parent. For non-nested components, the following two approaches can be used:

  • Communicate using context objects of both common parent components
  • How to use custom events

If we adopt the common parent between components to transfer, it will increase the coupling degree between the child component and the parent component. If the component level is deeper, it is not easy to find the common parent component between the two components. Of course, that sentence is not impossible.
Here we use custom events to achieve communication between non-nested components.

We need to use an events package:

npm install events --save

Create a new ev.js, introduce events package, and provide an event object for communication:

import { EventEmitter } from "events";
export default new EventEmitter();

App.js:

import React, { Component } from 'react';

import Foo from "./Foo";
import Boo from "./Boo";

import "./App.css";

export default class App extends Component{
    render(){
        return(
            <div>
                <Foo />
                <Boo />
            </div>
        );
    }
} 

Foo.js:

import React,{ Component } from "react";
import emitter from "./ev"

export default class Foo extends Component{
    constructor(props) {
        super(props);
        this.state = {
            msg:null,
        };
    }
    componentDidMount(){
        // Declare a custom event
        // After component loading is completed
        this.eventEmitter = emitter.addListener("callMe",(msg)=>{
            this.setState({
                msg
            })
        });
    }
    // Remove event listeners before component destruction
    componentWillUnmount(){
        emitter.removeListener(this.eventEmitter);
    }
    render(){
        return(
            <div>
                { this.state.msg }
                //I'm a non-nested number one.
            </div>
        );
    }
}

Boo.js:

import React,{ Component } from "react";
import emitter from "./ev"

export default class Boo extends Component{
    render(){
        const cb = (msg) => {
            return () => {
                // Triggering custom events
                emitter.emit("callMe","Hello")
            }
        }
        return(
            <div>
                //I'm non-nested No. 2
                <button onClick = { cb("blue") }>Click on me</button>
            </div>
        );
    }
}

Custom event is a typical publish/subscribe mode, which implements communication between components by adding listeners and trigger events to event objects.

summary

This paper summarizes several communication modes of components in React, which are:

  • Parent components communicate to child components: using props
  • Subcomponents communicate with parent components: using props callback
  • Cross-level component communication: using context objects
  • Communication between non-nested components: using event subscription

In fact, when communicating between components, these communication modes can be used. The difference lies only in the complexity and personal preferences of using the corresponding communication modes, and choosing the most suitable one. For example, communication through event subscription mode can be used not only among non-nested components, but also among cross-level components. Communication between non-nested components can also use context. The key is to choose the most appropriate way.
Of course, it is still too difficult to manage the communication between components by itself, so there are many state management tools, such as flux, redux and so on, which make the communication between components easier to track and manage.

Author: Charleylla
Links: https://www.jianshu.com/p/fb9...
Source: Brief Book
The copyright of the brief book belongs to the author. For any form of reprinting, please contact the author for authorization and indicate the source.

Keywords: Javascript React less npm

Added by cocpg on Thu, 16 May 2019 08:26:23 +0300