React FAQ: How do I get my components to “talk”?
A common question from React beginners is “How do I make two components communicate with each other in React?”
It depends. How do two parts of any program share information? Asking “What is our pattern for React?” is like asking “What is our pattern for Rails/Node/Laravel?” It should be apparent that there is not enough information for me to answer that question as you asked it. There are many, many ways to move data through your program— in React and in any other framework. Which patterns are more or less appropriate for your case depends greatly on the details of your case.
That said, data in React flows one direction— from parents to children in a ‘tree’ of components— unless you explicitly tell it to do otherwise. If your approach requires a lot of communication from child to parent, between peers, or between
any two arbitrary nodes in that component tree, you’re going to have a bad time.
Here’s a rule of thumb we use to avoid pain as React applications grow: components only talk to their children or to the “root” of the application.
With that in mind, here are three patterns you should consider for data flow in React:
1. Shared Models & Collections
The “root” component sets up all models and passes them down through the hierarchy in
props. It also listens for model updates and re-renders (maybe by calling
forceUpdate) on update. The re-render will cascade down to child components automatically.
- Appropriate when: a “family” of components share responsibility for displaying and editing one model or collection, e.g. a list of tasks and the form for adding a new task.
- Pros: Uses the natural flow of updates in React, so setup is simple. Rendering is easy to follow.
- Cons: Requires more advance knowledge of the relationship between components, making it less suitable for generic utility components. Since all of the components in the hierarchy have access to the shared model, code to update the model tends to spread throughout the component hierarchy, making it difficult to find later.
2. Global Dispatcher
The dispatcher will receive signals (custom events) from any component and forward the signal to any components which subscribe to that type of event.
- Appropriate when: you need maximum flexibility, and you don’t know in advance what components you’ll need to signal later. Works for general “utility” components— e.g. a message center which can display messages from
any component on the page.
- Pros: Flexible.
- Cons: Flexible enough to collapse at scale. You must strongly define your event API and naming conventions to avoid chaos.
3. Passing Down Event Handlers
Instead of passing down a shared model, the parent component passes down an event handler or callback that the child component calls when appropriate. The parent component defines the event handler, so it can update the parent’s state
and trigger a re-render.
- Appropriate for: imitating the behavior of native elements, e.g. passing an onChange handler to a component which pretends to be a form element.
- Pros: You can pretend that your fancy form element with all the bells and whistles is just a plain old text field.
- Cons: This becomes difficult to debug when you have many components on multiple levels which all pass handlers around.
- My recommendation: only use this if you are emulating the interface of a native element, and you only need to communicate directly from parent to child— never pass down an event handler through two or more levels.
Never use this one to communicate between peers.
In my experience, you will use #1 most commonly, followed by #2, and should only use #3 as I outlined above. There are limitless patterns, and my suggestions are only based on the applications that I’ve created with React, but I hope this
gives you a starting point.
At Revelry, we do custom software design, development and training.
Keep up with the things we share each week by subscribing to Coding Creativity.