React: Understand Communication Between Components

React: Understand Communication Between Components

React: Understand Communication Between Components

  • by admin
  • Web Development

React there are various ways we can pass data to/from components:

  • Render Props/Props
  • Context
  • React-Redux/Redux

In this post, we see in detail the various ways listed above we can communicate between components in React. In each case we will see how to achieve the 2 cases of communication:

  • Parent to Child
  • Child to Parent

Render Props/Props

One of the simplest and easiest ways to pass data to components is through props.

What is a prop?

You know we render our markup in components like this:

class C extends React.Component {
    render() {
        return (
            

C component

) } }

class P extends React.Component {
    render() {
        return (
            
        )
    }
}

would render:

C component 

C is actually rendered in P, P is C’s Parent component and C is the child component. Furthermore, P can render C like this:

class P extends React.Component {
    render() {
        return (
            
        )
    }
}

P added extra stuff in C element tag. It much like doing 

in HTML. The id is an attribute with a value "nnamdi". Each element is actually an instance of HTMLElement class.

 

The div tag above is parsed by the Browser rendering engine and instances of HTMLElement are generated for each tag. Each tag in HTML has a corresponding HTMLELement:

div -> HTMLDivElement
anchor -> HTMLAnchorElement
body -> HTMLBodyElement
button -> HTMLButtonElement
...

Each HTML*Elements above are subclass of HTMLElement. These HTML*Elements have methods and properties that can be used to manipulate data and for rendering purposes.

HTML*Element
    - appendChild
    - attributes
    - getAttribute
    - hasAttribute
    - innerHTML
    - innerText
    - setAttribute
...

In the case above, HTMLDivElement will be created:

const divElement = new HTMLDivElement()
divElement.setAttribute("name", "nnamdi")

The attribute name will be set in a props object like this:

props = {
    name: "nnamdi"
}

and passed to the C component class by React.

class C extends React.Component {
    constructor(props) { }
    ...
}

So the C component can access the value of name in the props object like this props.name.

class C extends React.Component {
    constructor(props) {
        log(this.props.name) // nnamdi
    }
    ...
}

React is JSX based. So the markup in P would be compiled to this:

 
    |
    |
    |
    |
    v
React.createElement(C, { name:"nnamdi" })

The first param is the element to render, the second param is an object literal holding the attributes in the element. The createElement returns an object literal containing the first param and the second param.

On rendering, React checks if the object literal is an actual element or a class. If its a class it instantiates the class using the new keyword passing in the props object and calls the render method.

const objType = React.createElement(C, { name:"nnamdi" })
if(IS_ELEMENT(objType)) {
    //...
}
else {
    if(IS_CLASS(objType)){
        const klass = objType.component
        // klass now holds the C class `class C extends React.Component {...}`
        const props = objType.props
        // props contains { name: "nnamdi" }, the attributes passed to the C component in the P component ``
        const klzz = new klass(props)
        // here, it(klass) is instantiated and props is passed to it.
        klzz.componentWillMount()
        klzz.render()
        klzz.componentDidMount()
        //...
    }
}

The above code represents what React actually does to render a Component or an element.

Note: the above is not complete, I just wrote it to demonstrate how our components receive the props argument.

We first check if the objType is an element or a class. The IS_ELEMENT(…) checks through a list of all HTML elements, if the objType matches one it returns true indicating the objType is an element. If it returns false therefore objType must be a class component, then, it destructures props and the class from the objType. Then it instantiates the class using new keyword and passing in the props.

With this P has managed to send data to the child C.

Then, how can C now send data back to its parent P? Its done through the use of render props.

what is render props? This is the concept whereby a function can be passed to child components as props. String, object, number, array can be passed via props to child components. Why not functions? It is done this way:

 {log("render props")}} />
class C extends React.Component {
    constructor(props){
        this.props.func()
    }
    render() {
        return (
            

{this.props.func()}

) } }

Here, we passed a function () => {log("render props")} to the C component via the func prop. Then, the C component can reference it via func in the props argument. Then, since we know it is a function we call by adding (), it logs render props in the console.

So we now know what a render props is. Let see how child components can utilize them to communicate with their parent components.

class P extends React.Component {
    constructor() {
        this.output = this.output.bind(this)
    }
    output() {
        log("With love from parent P land :)")
    }
    render() {
        return (
            
        )
    }
}
class C extends React.Component {
    constructor(props) {}
    render() {
        return (
            

C componentSend To Parent

) } }

Now what happened here? The P component has a method output bound to its instance, using bind(this) tells JS to run the function inside the class or object or function scope. So here running output outside the class P, it can still reference all the properties and method defined in P.

So we passed the method output to the C class component, to receive via func property in its props argument.

Now the C component has a button “Send To Parent” with an onclick event set to call the output method passed to it via func in its props argument object. So now this.props.func in C holds the output method in the P class:

this.props.func === new P().output

When the “Send To Parent” button in C is clicked. The this.props,func is called this.props.func(evt) being referenced to the output method in P, the output method is run output(evt). In our console we will see:

With love from parent P land :)

With that C has successfully communicated to its parent P. We can modify our code to make C send data to P.

class P extends React.Component {
    constructor() {
        this.output = this.output.bind(this)
        this.state = {
            count: 0
        }
    }
    output(evt) {
        log("With love from parent P land :)")
        this.setState({count: this.state.count + evt})
    }
    render() {
        return (
            

Count: {this.state.count}

 

) } }

class C extends React.Component {
    constructor(props) {}
    render() {
        return (
            

C componentthis.props.func(Math.random())}>Send To Parent

) } }

We added a state to P which holds a count property. In C when the “Send To Parent” button is clicked, it generates a random number and passes it to this.props.func. The output method in P catches the value in its evt argument and updates the count state with the received value by calling this.setState(…)

You see we have actually sent a value to P from C its child.

Summary

We saw in this section how to comm Parent-Child using props and Child-Parent using render props.

we changed the Parent state by passing a function to the Child component and invoking that function inside the Child component.

props and render props are the same thing, they have different concepts. render props is mainly used to share render logic between components. But in our case, we utilized it to comm data from child to Parent.

Context

The issue of passing props to deeply nested components in a tree, would both daunting and tiring for most devs. React brought a way to actually define our global data in one place and access from anywhere in our app, we are not talking of Redux or its cousins, we are talking of Context.

Context is used in React to share data across deeply nested components.

const ColorContext = React.createContext("yellow")
class App extends React.Component {
    render() {
        return (
            

) } }

class P extends React.Component {
    render() {
        return (
            {this.context}
            
        )
    }
}
class C extends React.Component {
    render() {
        return (
            
        )
    }
}
class Sub-C extends React.Component {
    render() {
        

{this.context}

} }

Here, we have three components in a tree: App -> P -> C -> Sub-C. App renders P, P renders C and C render Sub-C, we defined a context, ColorContext. P is enclosed with ColorContext.Provider, this provides access to the data defined in the ColorContext to all P component children.

The children can access the data defined in ColorContext by doing {this.context}.

In order to tell React we want to pass context from a parent component to the rest of its children we need to define two attributes in the parent class:

• childContextTypes and • getChildContext

To retrieve the context inside a child component, we need to define the contextTypes in the child.

You see here, the parent component defines a context which React use to pass data globally to its children, with that we have comm data to its children.

Now, how do the children comm data to its parent?

Children components can change the value context which will reflect globally across the nested components, the parent component inclusive. Learn more:

React-Redux/Redux

This is a remake of Context in React. Redux is a simple state management library to easily store your app state in one place and manipulate from anywhere in your app.

The React-Redux was created to make Redux easily pluggable to React apps.

Since the state is defined in one place, parent components can set or update the state and its children components will capture it: Parent-to-Child communication.

Likewise, the children components can set or update a state in the store and it will be captured by the parent components: Child-to-Parent communication.

// ...
const initialState = {
    count: 0
}
const countApp = function(state = initialState, action) {
    switch(action) {
        case 'ADD_COUNT':
            return {...state, state.count++}
        return state
    }
}
const store = createStore(countApp)
// src/P.js
class P extends React.Component {
    render() {
        return (Increment count
            

Count: {this.props.count}

) } }

 

/**
* maps dispatch to props
**/
function mapDispatchToProps(dispatch) {
    return {
        updateCount: (count)=> { dispatch({type: "ADD_COUNT",count})}
    }
}

 

/**
* maps redux state to props
**/
function mapStateToProps(state) {
    return {
        count: state.count
    }
}

 

export default connect(mapStateToProps, mapDispatchToProps)(P)

 

// src/C.js
class C extends React.Component {
    render() {
        return (Increment count
            

Count: {this.props.count}

) } }

/**
* maps dispatch to props
**/
function mapDispatchToProps(dispatch) {
    return {
        updateCount: (count)=> { dispatch({type: "ADD_COUNT",count})}
    }
}

 

/**
* maps redux state to props
**/
function mapStateToProps(state) {
    return {
        count: state.count
    }
}

 

export default connect(mapStateToProps, mapDispatchToProps)(C)

 

We created a central store that holds a count state. The P can update the count state and the child component C, will receive it. The C component can also update the count property and the parent component P will receive it.

 

They were able to connect to the central store by composing themselves into react-redux connect(...) function which returns a Higher-order component.

 

Conclusion

 

We saw in this post how communication between React components occur and the different ways by which we can achieve:

 

  • Child-Parent communication
  • Parent-Child communication

 

In the first section, we saw the use of props/render props, they do there stuff by using HTML-like attributes. Context uses a central store, so that parent and the children can both read and update. React-Redux/Redux does the same as Context, the components read/write from a central store.

 

If you have any question regarding this or anything I should add, correct or remove, feel free to comment, email or DM me. Thanks for reading 🙏

 

Tags: React