React Communication Between Components

When you move to React as an experienced developer, there are two things, that I would say, are the hardest parts to get used to. The first is the JSX/TSX. Figuring out the syntax, how it works and fits into your code and how to get used to it being mixed into your class and not separated out. The acclimation is slow at first, then it will become normal and then a time will come when you look at going without React and you shutter at the thought of having a separate html file.

Now the second, maybe even harder part, is getting used to communication in React. This too will become something wonderful once you get used to it, as long as you don’t fight it and embrace it. I have not, though, ever found a comprehensive guide on how communication in React should occur, and have had to fumble through and learn these different types of communication and when they should and shouldn’t be used. I’m sure I have much more to learn, but I hope this primer will allow people to quickly ramp up on types of communication, both basic and advanced and then embrace it. And for those that are already experienced, it can be used as a quick reference.

Parent to Child

Let’s get started. Almost every component you will make in React will need communication between parent and child. This is a foundational part of using React, so it is important to understand how to do it well and what to use when. There are multiple ways to do this communication, which will normally be done via props. Here is a very basic example:

import * as React from 'react';
export class Parent extends React.Component<any, any> {
    render() {
        return <Child info="From Parent" />
    };
}

class Child extends React.Component<{ info: string }, any>{
    render() {
        return <span>{this.props.info}</span>
    }
}

Child to Parent ‘event’ (i.e. onClick)

A very familiar model, which is more like the C# and JS/HTML, is to have the child tell the parent about changes and the parent and the child both track them independently. This is a very simple and familiar “event” type paradigm and still allows the parent to always know what the child is doing, however it it doesn’t allow the parent to override or change what has happened in the child.

import * as React from 'react';
export class Parent extends React.Component<any, never> {
    onSubmit = () => {
        console.log('Button Clicked');
    }
    render() {
        return <Child onSubmit={this.onClick} />
    };
}

class Child extends React.Component<{ onClick: () => void }, any>{
    render() {
        return <button onClick={this.props.onClick}>Click Me</button>
    }
}

Here the child has a button, when the button is clicked it calls the onClick method passed down as a prop. The parent then knows the button is clicked and does some event, such as submitting a form, though if you do want to do a basic form, you may want to look at React Forms and see if that meets your needs. No sense in reinventing the wheel.

Communication Between Siblings

Now you’ve seen how to communicate from a parent to a child and from a child to a parent, the next challenge is how to communicate between two peer components, two siblings. The key here is to communicate via the parent:

import * as React from "react";

export class Parent extends React.Component<any, {counter: number}> {
  state = { counter: 0 };

  onChange = (addToCounter: number) => {
    this.setState({counter: this.state.counter + addToCounter})
  };

  render() {
    return (
      <>
        <ClickingChild onChange={this.onChange} />
        <br />
        <MessageChild message={`Clicked ${this.state.counter} `} />
      </>
    );
  }
}

interface ClickingChildProps {
  onChange: (addToCounter: number) => void;
}

class ClickingChild extends React.Component<ClickingChildProps, never> {
  render() {
    return (
      <>
        <button onClick={() => this.props.onChange(1)}>+1</button>
        <button onClick={() => this.props.onChange(5)}>+5</button>
        <button onClick={() => this.props.onChange(10)}>+10</button>
      </>
    );
  }
}

interface MessageChildProps {
  message: string;
}

class MessageChild extends React.Component<MessageChildProps, never> {
  render() {
    return (
      <span>{this.props.message}</span>
    );
  }
}

This is a fine model if the child is fairly independent children that just need to notify each other. This can be good model depending on the use case, such as having a control that raises an event, like the button and like one component having the data and another one doing some sort of action or visualization on it. However, if you start having a situation where the child gives the parent info, the parent processes it and sends info back down to the child, or the parent will need to save and restore the data to the child component (such as saving and loading of preferences or form data) you should look at the next option.

This ends the basic examples that are much easier to find tutorials on. Next we jump into the more complicated situations, such as when the parent needs to know the state of the child when it does an action, or the parent needs to be able to change the child. Or the two components are not in a direct parent and child relationship. Or perhaps your parent component needs to tell the child component its time to do something, like perform a validation. There are multiple other options for how to do this communication that the above simple examples don’t cover, that I will go over here.

Child Being Controlled by Parent (Props Driven)

My favorite option, and the one that I would say is to have the parent control the state of the child, which has some nice advantages, such as being able to save and restore state so that when a user leaves the page and comes back the state is as they left it. It allows the parent always know what the child is doing and never need to ask. It also allows the parent to supervise and reject what the child wants to do. This one deserved its own post: Children Obey Your Parents: React Design Pattern.

The Double Pointer

This option is a workaround of how React works and should only be used if the above options are completely out of the question. I like to call it the “Double Pointer”. This can be used when you have a child with an instance method inside of it that you need to call for some purpose, but again, first try one of the above methods. Basically, in JavaScript terms, what you are doing is passing a callback to the child via the props that takes a callback as a parameter for the real function we want This also deserved its own post: React and the Double Pointer.

Flux, Redux, etc

There are multiple libraries that can be used with React that allow for saving and sharing state. These should not be used just to ease parent and child communication. There are multiple options, such as Flux, Redux and MobX to store and read state and facilitating of the observer design pattern. In my opinion, these should not be used just to facilitate communication when one of the standard ways above would work just fine. However if you need persistent state between components or potentially as a way to get around components with estranged relationships, such as the cousin of a component five times removed, then this may be a valid option for you. It is also a bad fit for any component being made to share with others, such as npm packages.

Message Bus

The final one I can think of, is to use some sort of message bus system. I’m not going to write about it right now, but the idea is having a subscriber/publisher model of data changes, kind of like Flux, except without a store and instead a subscriber. I would say this wouldn’t be a normal thing to need in React, but if you are not using Flux or Redux and the relationships between the components are either two distant or estranged, it is a final resort that isn’t too bad. I’ll try to get a co-worker of mine to write about this one and link it here 😉

I will work on keeping this post updated as I learn more, so If you can think of any other options, let me know what they are and when they would be fitting to use.