“Good communication is just as stimulating as black coffee, and just as hard to sleep after.”
-Anne Morrow Lindbergh

One of the first issues while using React is figuring out how different components communicate with each other. Whether it’s from parent to child, child to parent, or child to child, there are a plethora of different ways to do it.

Table of Contents

  1. Props
  2. Refs
  3. Callback Functions
  4. Parent Components
  5. Observer Pattern
  6. Global Variables
  7. Flux/Redux
  8. Context

Props

This is the most fundamental way of passing data from a Parent to a Child. You pretty much can’t use React without knowing this. But just in case, here’s a recap. Link to docs here.

class Parent extends Component {
  
    state = {
      a: 'a',
      b: 'b',
      c: 'c'
    }

  veryImportantFunction () {
      console.log('important!!');
    }

  render() {
      return (
        <Child importantFunction={this.veryImportantFunction}  parentState={this.state} />
    )
  }
}
class Child extends Component {
    constructor(props) {
      super(props);
    }

  onClickMe() {
      this.props.importantFunction()
      console.log(this.props.parentState);
    }

  render() {
      return (
        <button onClick={this.onClickMe.bind(this)}>click me</button>
    )
  }
}

By passing props into the child in the parent, the child will have access it inside this.props. Props is probably the most common way of passing data from a parent to a child, or if a child needs to call a function of the parent.

Refs

So we know how to pass data from a parent to a child. But what if we need to call a function of the child from inside the parent? Using refs (also known as a instance method), we can do just that!

class Child extends Component {
    childFn() {
      return 'hello world';
    }
}
class Parent extends Component {
    render() {
      return (
        <Child  ref={child => this.child = child}  />
    );
  }

  componentDidMount() {
      const phrase = this.child.childFn();
      // phrase === 'hello world'
    }
}

Refs are special tags that can be added to any React Component. It creates a reference to itself that you can assign to it to pretty much any variable in the parent component. (In this case we chose this.child as the variable that we’re assigning the reference (child) to. Now using this reference, we can call functions, and even have access to variables. Docs

Callback Functions

Our first two ways of communicating was Parent to Child. If we need to pass data from the CHILD to the parent, we can use something callbacks, which is really just using props in a different way. Instead of passing data into the prop, we will be passing a function into the prop. That way, our child can call the function with its data as the argument.

class Parent extends Component {
  
    importantFn (data) {
      console.log(data);
    }

  render() {
      return (
        <Child someFunc={this.importantFn} />
    )
  }
}

The child component could just call it the same way as using props:

this.props.someFunc('all the data can go here');

If using React, it’s good practice to have the function declared in the propTypes:

Child.propTypes = {
    someFunc: React.PropTypes.func
  }

This is not necessary if using Preact.

Parent Components

If two sibling components need to communicate with each other, the easiest way is to let them communicate through the parent. We can use some of the previous strategies to implement this.

class Parent extends Component {

render() {
  return (
    <div>
      <SiblingA propState={this.state.stateA} someFunc={this.sibAfunc} />
    <SiblingB propState={this.state.stateB} someFunc={this.sibBfunc} />
  </div>
)

}
}

Now we can use different functions to pass data from one sibling to the parent then back down to the next sibling.

Observer Pattern

The Observer Pattern is not a strictly React strategy. It is a design pattern in software which a component can receive messages by subscribing to an object. No sibling or parent is necessary in this relationship, it can work with any set of components.

In React, how this would work is one component would SUBSCRIBE to another component and the other component would PUBLISH messages to the other ones. You can have more than one subscription set up at once. Components would typically subscribe in componentDidMount. Forgetting to unsubscribe could lead to memory leaks!

Some libraries that can implement this:

PubSubJS, EventEmitter, MicroEvent.js, mobx. It can also be done using RxJS.

Global Variables

Global variables go against every best practice in the book. But they definitely work in a pinch (if you’re testing or something). I highly recommend not using it in a production environment. (In fact, in strict mode, global variables don’t even work).

But if you must, you can define these variables in a lifecycle or the render method using something like window.someFunc . It’ll bind it to the window object and you’ll have access to it pretty much anywhere.

Flux/Redux

If you feel like you’re needing to pass data top to bottom or bottom to top a lot, maybe some of the previous strategies aren’t for you. Depending on how much or how often you need to do it, if you’re just passing data down using props and passing the data back up using callbacks, you may be doing something we call “prop drilling,” which is just using props to go down into multiple components.

The flux architecture (most commonly: Redux) is a very common solution to solving this problem. I already wrote a blog post regarding Redux here.

Context

If you feel like you have a fairly small project, and feel that implementing Redux is a little too heavy handed, React has recently implemented a non-beta version of their context API. This API allows us to pass data down to any child.

The React "context" API consists of three main parts:

  1. React.createContext which is passed the initial value, it returns an object with a Provider and a Consumer.
  2. The provider component is used higher in the tree and accepts a prop called value.
  3. The consumer component can be used anywhere below the provider and accepts a prop called children. Children must be a function that accepts a value and returns a react element.

Conclusion

So there’s a list of different strategies that you can use to pass data to different components. Some of these are more best-practicey than others, but all of them have their time and place. For larger projects, Redux may be the way you want to go. Something direct, props might be for you. If you need to pass data down once, context might be the best for that job. Regardless, just make an active decision on what you think is the best tool is for the job and everything will work out.