Problem
I’m new to ReactJS and am having some trouble with a problem I’m having.
list />, Filters />, and TopBar />, thus when I alter Filters /> settings, I obviously want some method in list /> to update my view.
Is there a way to make those three components communicate with one another, or do I require some sort of global data model that I can tweak?
Asked by woutr_be
Solution #1
The ideal strategy will be determined by how you intend to arrange those components. Here are a few examples that come to mind right now:
There could be additional possibilities that I’m overlooking. Please let me know if yours does not fit inside these parameters. Here are some rudimentary examples of how I’ve dealt with the first two scenarios:
You could send a handler from List to Filters and then call it on the onChange event to filter the list with the current value.
For #1, I used JSFiddle.
/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
var content;
if (displayedItems.length > 0) {
var items = displayedItems.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<h4>Results</h4>
{content}
</div>
);
}
});
React.renderComponent(<List />, document.body);
Similar to example #1, however this time the parent component will give the handler function to Filters /> and the filtered list to List />. This solution appeals to me more because it separates the List /> from the Filters />.
For #2, I used JSFiddle.
/** @jsx React.DOM */
var Filters = React.createClass({
handleFilterChange: function() {
var value = this.refs.filterInput.getDOMNode().value;
this.props.updateFilter(value);
},
render: function() {
return <input type="text" ref="filterInput" onChange={this.handleFilterChange} placeholder="Filter" />;
}
});
var List = React.createClass({
render: function() {
var content;
if (this.props.items.length > 0) {
var items = this.props.items.map(function(item) {
return <li>{item}</li>;
});
content = <ul>{items}</ul>
} else {
content = <p>No items matching this filter</p>;
}
return (
<div className="results">
<h4>Results</h4>
{content}
</div>
);
}
});
var ListContainer = React.createClass({
getInitialState: function() {
return {
listItems: ['Chicago', 'New York', 'Tokyo', 'London', 'San Francisco', 'Amsterdam', 'Hong Kong'],
nameFilter: ''
};
},
handleFilterUpdate: function(filterValue) {
this.setState({
nameFilter: filterValue
});
},
render: function() {
var displayedItems = this.state.listItems.filter(function(item) {
var match = item.toLowerCase().indexOf(this.state.nameFilter.toLowerCase());
return (match !== -1);
}.bind(this));
return (
<div>
<Filters updateFilter={this.handleFilterUpdate} />
<List items={displayedItems} />
</div>
);
}
});
React.renderComponent(<ListContainer />, document.body);
When components can’t communicate with each other because of a parent-child relationship, the manual suggests using a global event system.
Answered by Michael LaCroix
Solution #2
Components can communicate in a variety of ways. Some may be appropriate for your needs. Here’s a list of some that I’ve discovered to be useful.
const Child = ({fromChildToParentCallback}) => (
<div onClick={() => fromChildToParentCallback(42)}>
Click me
</div>
);
class Parent extends React.Component {
receiveChildValue = (value) => {
console.log("Parent received value from child: " + value); // value is 42
};
render() {
return (
<Child fromChildToParentCallback={this.receiveChildValue}/>
)
}
}
The child component will provide a value to a callback provided by the parent, and the parent will be able to get the value provided by the children in the parent.
If you’re creating an app feature or page, it’s best to have a single parent that manages callbacks and state (also known as a container or smart component) and all children that are stateless and only report to the parent. This way, you can easily “share” the parent’s current status with any child who need it.
React Context permits to hold state at the root of your component hierarchy, and be able to inject this state easily into very deeply nested components, without the hassle to have to pass down props to every intermediate components.
Context was formerly an experimental feature, however in React 16.3, a new API is introduced.
const AppContext = React.createContext(null)
class App extends React.Component {
render() {
return (
<AppContext.Provider value={{language: "en",userId: 42}}>
<div>
...
<SomeDeeplyNestedComponent/>
...
</div>
</AppContext.Provider>
)
}
};
const SomeDeeplyNestedComponent = () => (
<AppContext.Consumer>
{({language}) => <div>App language is currently {language}</div>}
</AppContext.Consumer>
);
The render prop / children function pattern is being used by the customer.
For more information, see this blog post.
Prior to React 16.3, I’d propose using react-broadcast, which has a very similar API, and using the old context API.
When you want to maintain two components near together to communicate using basic functions, like in conventional parent/child, but you don’t want them to have a parent/child relationship in the DOM due to visual / CSS constraints, use a portal (like z-index, opacity…).
You can utilize a “portal” in this instance. There are several react libraries that employ portals, which are commonly used for modals, popups, and tooltips.
Consider the following:
<div className="a">
a content
<Portal target="body">
<div className="b">
b content
</div>
</Portal>
</div>
When rendered inside reactAppContainer, the DOM could look like this:
<body>
<div id="reactAppContainer">
<div className="a">
a content
</div>
</div>
<div className="b">
b content
</div>
</body>
More details here
You create a slot and then fill it with data from another part of your render tree.
import { Slot, Fill } from 'react-slot-fill';
const Toolbar = (props) =>
<div>
<Slot name="ToolbarContent" />
</div>
export default Toolbar;
export const FillToolbar = ({children}) =>
<Fill name="ToolbarContent">
{children}
</Fill>
This is identical to portals, except that instead of rendering a new dom node, the filled content will be rendered in a slot you specify (often a children of document.body)
Check react-slot-fill library
According to the documentation for React:
You can use a variety of methods to set up an event bus. You may just construct an array of listeners, and when the event is published, all of the listeners will receive it. You may also use a library like EventEmitter or PostalJs.
Flux is an event bus with the exception that the event receivers are stores. The state is managed outside of Re, therefore it’s identical to the fundamental event bus system.
The first Flux implementation appears to be a clumsy attempt at Event-sourcing.
Redux is for me the Flux implementation that is the closest from event-sourcing, an benefits many of event-sourcing advantages like the ability to time-travel. It is not strictly linked to React and can also be used with other functional view libraries.
The Redux video instruction by Egghead is quite helpful and shows how it works within (it really is simple).
Cursors are extensively utilized in React projects and come from ClojureScript/Om. They provide state management outside of React and give various components read/write access to the same area of the state without requiring knowledge of the component tree.
ImmutableJS, React-cursors, and Omniscient are just a few examples of implementations.
Edit 2016: It appears that while cursors work well for smaller programs, they do not scale well for larger apps. Cursors are no longer available in Om Next (despite the fact that it was Om who first introduced the concept).
The Elm architecture is a suggested architecture for the Elm programming language. Elm architecture can be done with React as well, even though Elm isn’t ReactJS.
The Elm architecture was implemented using React by Dan Abramov, the inventor of Redux.
Both Redux and Elm are fantastic and tend to empower event-sourcing principles on the frontend, allowing for time-travel debugging, undo/redo, and replay, among other things.
The key distinction between Redux and Elm is that Elm adheres to a stricter state management philosophy. Local component state or mount/unmount hooks are not allowed in Elm, and all DOM changes must be triggered by global state changes. Elm architecture propose a scalable approach that permits to handle ALL the state inside a single immutable object, while Redux propose an approach that invites you to handle MOST of the state in a single immutable object.
While Elm’s conceptual model is highly clean, and the architecture allows it to scale well on large apps, simple things like giving an input emphasis after mounting it or connecting with an existing library with an imperative interface might be tricky or involve extra boilerplate in practice (ie JQuery plugin). This is a related topic.
Elm architecture also requires more boilerplate code. Although it isn’t overly lengthy or difficult to create, I believe the Elm design is better suited to statically typed languages.
To handle communication between components, libraries like RxJS, BaconJS, or Kefir can be used to create FRP streams.
You may, for example, try Rx-React
is very similar to using the signals provided by the ELM language.
The CycleJS framework does not employ ReactJS, instead opting for vdom. It share a lot of similarities with the Elm architecture (but is more easy to use in real life because it allows vdom hooks) and it uses RxJs extensively instead of functions, and can be a good source of inspiration if you want to use FRP with React. CycleJs Egghead videos are nice to understand how it works.
CSP (Communicating Sequential Processes) are currently popular (thanks to Go/goroutines and core.async/ClojureScript), but JS-CSP allows you to utilize them in javascript as well.
A video by James Long demonstrates how it may be utilized with React.
A saga, often known as a “process manager,” is a backend idea from the DDD / EventSourcing / CQRS universe. The redux-saga project popularized it, primarily as an alternative for redux-thunk for handling side-effects (ie API calls etc). The majority of people believe it is solely used for side effects, although it is actually used to decouple components.
Because the saga emits Flux actions at the conclusion, it is more of a complement to a Flux architecture (or Redux) than a completely new communication mechanism. The concept is that if you have widget1 and widget2 and want to decouple them, you won’t be able to fire action on widget2 from widget1. So you make widget1 only fire actions that are directed at it, and the saga is a “background process” that listens for widget1 actions and may dispatch actions that are directed at widget2. The storey is the point of connection between the two widgets, but they are still detached.
Take a look at my response here if you’re interested.
Check out the forks of this repository to see an example of the same small project utilizing these different styles.
I’m not sure what the ideal long-term choice is, but I really like how Flux appears when it comes to event-sourcing.
Take a look at this extremely educational blog if you’re unfamiliar with event-sourcing concepts: It is a must-read to understand why Flux is good since it turns the database inside out using apache Samza (but this could apply to FRP as well)
I believe the community thinks that Redux is the most promising Flux implementation, as hot reloading will allow for a more productive developer experience over time. It’s possible to do impressive livecoding like Bret Victor’s Inventing on Principle video!
Answered by Sebastien Lorber
Solution #3
OK, there are a few ways to do it, but instead of giving you a quick solution only for this case, I’d rather focus on using store with Redux, which makes your life much easier in these situations. Using pure React will end up causing problems in a large application, and communicating between components becomes increasingly difficult as the application grows…
So, what exactly does Redux do for you?
Redux is a type of local storage in your app that you can use whenever you require data to be used in several places throughout your app…
Essentially, the Redux concept is based on flux, but with a few key differences, such as the concept of having a single source of truth by constructing only one store…
Take a look at the graph below to understand how Flux and Redux differ…
If your app requires communication between components, you should consider using Redux from the start…
To begin, you might want to read the following words from the Redux documentation:
Answered by Alireza
Solution #4
This is how I dealt with the situation. Let’s pretend you have a month select and a day select. The number of days is determined by the month chosen.
A third item, the left panel, owns both lists. Both select> and leftPanel div> are children of the leftPanel div>. It’s a game with the LeftPanel component’s callbacks and handlers.
Simply put the code into two different files and run the index.html to test it. After then, choose a month and observe how the number of days changes.
dates.js
/** @jsx React.DOM */
var monthsLength = [0,31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
var MONTHS_ARR = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
var DayNumber = React.createClass({
render: function() {
return (
<option value={this.props.dayNum}>{this.props.dayNum}</option>
);
}
});
var DaysList = React.createClass({
getInitialState: function() {
return {numOfDays: 30};
},
handleMonthUpdate: function(newMonthix) {
this.state.numOfDays = monthsLength[newMonthix];
console.log("Setting days to " + monthsLength[newMonthix] + " month = " + newMonthix);
this.forceUpdate();
},
handleDaySelection: function(evt) {
this.props.dateHandler(evt.target.value);
},
componentDidMount: function() {
this.props.readyCallback(this.handleMonthUpdate)
},
render: function() {
var dayNodes = [];
for (i = 1; i <= this.state.numOfDays; i++) {
dayNodes = dayNodes.concat([<DayNumber dayNum={i} />]);
}
return (
<select id={this.props.id} onChange = {this.handleDaySelection}>
<option value="" disabled defaultValue>Day</option>
{dayNodes}
</select>
);
}
});
var Month = React.createClass({
render: function() {
return (
<option value={this.props.monthIx}>{this.props.month}</option>
);
}
});
var MonthsList = React.createClass({
handleUpdate: function(evt) {
console.log("Local handler:" + this.props.id + " VAL= " + evt.target.value);
this.props.dateHandler(evt.target.value);
return false;
},
render: function() {
var monthIx = 0;
var monthNodes = this.props.data.map(function (month) {
monthIx++;
return (
<Month month={month} monthIx={monthIx} />
);
});
return (
<select id = {this.props.id} onChange = {this.handleUpdate}>
<option value="" disabled defaultValue>Month</option>
{monthNodes}
</select>
);
}
});
var LeftPanel = React.createClass({
dayRefresh: function(newMonth) {
// Nothing - will be replaced
},
daysReady: function(refreshCallback) {
console.log("Regisering days list");
this.dayRefresh = refreshCallback;
},
handleMonthChange: function(monthIx) {
console.log("New month");
this.dayRefresh(monthIx);
},
handleDayChange: function(dayIx) {
console.log("New DAY: " + dayIx);
},
render: function() {
return(
<div id="orderDetails">
<DaysList id="dayPicker" dateHandler={this.handleDayChange} readyCallback = {this.daysReady} />
<MonthsList data={MONTHS_ARR} id="monthPicker" dateHandler={this.handleMonthChange} />
</div>
);
}
});
React.renderComponent(
<LeftPanel />,
document.getElementById('leftPanel')
);
And index.html is the HTML for operating the left panel component.
<!DOCTYPE html>
<html>
<head>
<title>Dates</title>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
<script src="//fb.me/react-0.11.1.js"></script>
<script src="//fb.me/JSXTransformer-0.11.1.js"></script>
</head>
<style>
#dayPicker {
position: relative;
top: 97px;
left: 20px;
width: 60px;
height: 17px;
}
#monthPicker {
position: relative;
top: 97px;
left: 22px;
width: 95px;
height: 17px;
}
select {
font-size: 11px;
}
</style>
<body>
<div id="leftPanel">
</div>
<script type="text/jsx" src="dates.js"></script>
</body>
</html>
Answered by Skulas
Solution #5
I see that the question has previously been answered, however if you’d need extra information, there are a total of three cases of component communication:
Answered by Kaloyan Kosev
Post is based on https://stackoverflow.com/questions/21285923/reactjs-two-components-communicating