Child-to-parent data flow - reactjs

I'm new to React and still learning the basics. Looking at the tutorial here:
http://facebook.github.io/react/docs/tutorial.html#callbacks-as-props
It's explaining how to make a parent respond to events on a child. Here's the relevant bit of code:
var CommentBox = React.createClass({
handleCommentSubmit: function(comment) {
// TODO: submit to the server and refresh the list
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm
onCommentSubmit={this.handleCommentSubmit}
/>
</div>
);
}
}
And then in the CommentForm component:
var CommentForm = React.createClass({
handleSubmit: function() {
var author = this.refs.author.getDOMNode().value.trim();
var text = this.refs.text.getDOMNode().value.trim();
this.props.onCommentSubmit({author: author, text: text});
this.refs.author.getDOMNode().value = '';
this.refs.text.getDOMNode().value = '';
return false;
},
}
If I'm understanding this correctly, instantiating a <CommentForm/> (without any attributes) would create a broken reference when CommentForm.handleSubmit() tries to call this.props.onCommentSubmit(). The parent must rather do <CommentForm onCommentSubmit={some.function}/>. Is this the official way to do child-to-parent data flow in React? Or is there a more loosely-coupled way? I guess I was hoping for something more akin to Backbone's trigger() and on() functions where one view can change which events it listens to without necessarily creating runtime errors in other views.

This is probably the most common form of passing data between components in React. While it's more tightly-coupled than an event-based solution, it's also more explicit (due to less indirection), and React focuses a lot on removing bugs centered around problems with data flow (which can be a problem in large Backbone applications).
Reusable components will often provide an API via their properties. For example, in the CommentForm, it will call the function passed to onCommentSubmit. Components can make this API more explicit by using the propTypes hash. Required properties can be declared with .isRequired, while optional properties leave it off. Components that should function without certain properties should check for the existence of those properties in their handlers, e.g.:
var CommentForm = React.createClass({
handleSubmit: function() {
if (this.props.onCommentSubmit) {
// ...
this.props.onCommentSubmit(...);
}
},
}
Just as passing callbacks around is the most common form of dealing with asynchronous data in Node.js applications, passing functions as properties is the lowest-common-denominator to parent-child component communication in React, and is the best way to ensure any components you build will work with other folks' code. But, just as Node has EventEmitters, promises, fibers, and more, there are plenty of methods you can use to effectively communicate data flow in your React applications. One popular architecture is Flux which offers some more flexibility on the application level while still keeping a clear, one-way data flow; another is the use of an event bus (e.g. with EventEmitter2). Keep in mind, though, what guarantees you're giving up by moving to a more flexible, loosely coupled architecture.

Related

"Private" Communication with React Flux dispatcher

I'm attempting to decide on the best way to handle "private" communication between a store and a react component that can exist in multiple places. I've got two instances of the same component, both of which need to communicate with the same store. But, they need to behave independently. So when one gets an error message, the other should not display it.
The issue is, because my store and my dispatcher are both singletons, both instances of the component are getting the message. Both instances are listening to the store's events, so if that's a singleton, both will receive error events.
And, both instances are using the same dispatcher, so even if I had multiple instances of the store, all of them would be getting the same message at the same time, so they would all still react to it and emit a response.
I've been looking around but I haven't seen much about handling this problem, has anyone come across anything like it? I was thinking I could pass along a reference to the caller in the action, move it through to the event response and then check it again in the component, like so:
// my_component.js
import dispatcher from 'dispatcher';
import myStore from 'my_store';
const myComponent = React.createClass({
handleNewValue: function(){
dispatcher.dispatch({actionType:'check_error', value: 'value', caller: this});
}
componentWillMount: function(){
myStore.on('show_error', function(message){
if (message.caller === this){
doThing(message.error);
}
}
}
...
}
And:
// my_store.js
import dispatcher from 'dispatcher';
...
dispatcher.register(function(payload){
if (actionType === 'check_error'){
this.trigger('show_error', {error: 'is_bad', caller: payload.caller});
}
}
But for complex systems, this seems like it could get out of hand. It also seems more manual than I would like. Any thoughts would be appreciated!

Re-rendering an app using ReactDOM.render() rather than within its container component -- anti-pattern?

I have a single object state-driven application whose state dispatch/subscribe logic is to be kept separate from the React 'flow' (i.e. no helpers like React-Redux bindings).
When the state changes, my app re-renders.
Is there any difference between the following two implementations, or any anti-pattern concerns? (sorry to anyone upset I'm not using JSX)
var myElementClass = React.createClass(
render : function() {
//make use of this.props.state...
}
);
var myAppContainerComponent = React.createElement(
myElementClass,
{state : dataStore.getState()}
);
dataStore.onChange(function(){
ReactDOM.render(myAppContainerComponent, someDOMContainer);
});
vs...
var myElementClass = React.createClass(
componentDidMount : function() {
var self = this;
this.props.store.onChange(function(){
self.setState(self.props.store.getState());
});
},
render : function() {
//make use of this.state...
}
);
var myAppContainerComponent = React.createElement(
myElementClass,
{store : dataStore}
);
ReactDOM.render(myAppContainerComponent, someDOMContainer);
The first forces the app-wide re-render from 'outside', i.e. using ReactDOM. The second does the same thing within the container app.
I've done some performance tests and don't actually see a difference. Will I run in to issues down the road? Is hitting ReactDOM.render() many times an issue?
I know some people will comment that both ways are possibly expensive as they're each re-rendering the whole app (isn't that what React is for ;) ), but that's out of scope of this question.
There is no big difference when you have a few components, but when your app grows large, re-rendering from the top is going to introduce a slowdown. This is why I would recommend subscribing individual components to the store and only using setState() if the part of the state that they care about has changed. This way your components will be much more performant as the app grows.
Finally, we don’t recommend you to use store.subscribe() directly. There is a whole library called React Redux which does the subscription for you! When you use connect() from it, it wraps your components with that setState() logic so you don’t have to write it, and you only need to specify the parts of the state that your components care about. Also, React Redux is more efficient than the code you would write by hand because it contains many optimizations.
I think your components should be a pure function of whatever state happens to be outside of it, but not aware of that state (well, as "pure" as it can reasonably be).
I see a "leaky implementation" here in the second example, meaning that when you have:
componentDidMount : function() {
var self = this;
this.props.store.onChange(function(){
self.setState(self.props.store.getState());
});
},
you are conflating the component itself with the function that is supposed to cause re-rendering of the component.
Your first implementation seems a lot more appropriate to me. Additionally, the first implementation is a lot more re-usable.
In your second example, what if you want to change the structure of your data store, rendering, etc.? Then you may very well have to go into every single component and change it as well.
Bottom line, I definitely like the first implementation better.

Binding to event handler that calls setState in ComponentDidMount produces warning

I'm using jQuery to create event bindings in a ReactJS component's componentDidMount function, which seems like the right place to do this.
$('body').on('defaultSearchContext.registerQueryEditor', (function(_this) {
return function(event, component) {
_this.setState({
queryEditors: _this.state.queryEditors.concat([component])
});
};
})(this));
This code isn't actually run on componentDidMount, it's simply setting up the binding that later calls setState when the event fires. However, this generates the following warning every time this event triggers, which pollutes my console with dozens of warnings:
Warning: setState(...): Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
I have tried moving the setState code to a separate function like onEvent and calling that from the binding in componentDidMount but the warning is still produced.
Ideally, I'd like to create the binding in the proper place, indeed, there is some issue with doing it in componentDidMount. If not, I'd like to know if it's possible to silence the warning, or whether I should perhaps file a bug for ReactJS itself. If it helps, I'm using ReactJS 0.14.3 (latest at this time).
This is similar to, but not the same as React Js onClick inside render. In that case, the solution is to return an anonymous function to onClick, but that doesn't seem applicable to my situation.
You are trying to coordinate events between independent components. This is a fairly standard thing to do, and it doesn't require DOM events. The standard practice for doing this in React is to use a store/dispatcher pattern like Redux or Flux (I personally prefer redux). However, if this is part of a larger, not-completely-React application, then this may not be possible. If it is just for a small piece of an React app, it may still be overkill.
All you need is an object to coordinate events. An event is just a collection of callbacks, possibly typed or keyed. This requires nothing more than an object shared between two places. DOM Events are overkill; jQuery is overkill. You just need to trigger a callback.
This is a VERY SIMPLE event coordinator.
let simpleEventCoordinator = {
callbacks: new Map(),
getHandler(eventKey) {
let handler = this.callbacks.get(eventKey);
if (!handler) {
handler = new Set();
this.callbacks.set(eventKey, handler);
}
return handler;
},
registerCallback(eventKey, callback) {
this.getHandler(eventKey).add(callback);
},
removeCallback(eventKey, callback) {
this.getHandler(eventKey).delete(callback);
},
trigger(eventKey, data) {
this.getHandler(eventKey).forEach(c => c(data));
}
Keep a map of callbacks, which will be nameOfEvent => callback(). Call them when asked. Pretty straightforward.
I know nothing about how your components are structured, but you said they are independent. Let's say they look like this:
React.render((
<div>
<QueryManager />
<button onClick={() => simpleEvent.trigger('event')}>{'Update'}</button>
</div>
), document.body);
This is all your component needs to handle this event
componentDidMount() {
simpleEvent.registerCallback('event', this.update);
}
componentWillUnmount() {
simpleEvent.removeCallback('event', this.update);
}
update() {
//do some stuff
}
I've put together a very simple codepen demonstrating this.
Looking at the source code of where that warning is coming from, it appears that if some reference is maintained before an update is about to happen, it throws that warning. So maybe the way your mixing the jQuery events and react is creating a memory leak? Its hard to say exactly because of the lack of surrounding code to your snippet what else could be going on.

How to load initial data in Flux, but only if a component needs it

I am trying to adhere to the Flux pattern as best I can, but I must be missing something, because I can't figure how to get this to work without using a hack or doing something that feels wrong for the pattern.
I have an application that is segmented by big buckets of data. Let's say its a Resource Management app, and each Department in the company has a different set of resources. Users of the app will always go to whatever department they are a member of, and stay there. Some people may have multiple departments they manage.
So, at the top of this app there will be a way to select what Department you are working with.
The key here is that I cannot really display anything useful until the list of departments has loaded and I have selected which department to display (which will just be the first item in the list for now).
To accomplish this, the root component of the app looks at the DepartmentStore to determine if it is currently loading or not. If it is loading, it does not even bother rendering the component. Something like this:
function initApp(projectSlug) {
// prefetch some data!
DeptActions.getAll();
}
function getStateFromStores() {
return {
loading: DeptStore.getLoading()
};
}
export default React.createClass({
getInitialState: function() {
return getStateFromStores();
},
componentDidMount: function() {
initApp();
DeptStore.addChangeListener(this._deptChange);
},
componentWillUnmount: function() {
DeptStore.removeChangeListener(this._deptChange);
},
_deptChange: function() {
this.setState(getStateFromStores());
},
render: function() {
return (
this.state.projectsLoading
? <div>Loading...</div>
: (<div>
<Header/>
<div className="body-content">
<div className="grid-container">
{this.props.children}
</div>
</div>
<Footer/>
</div>)
);
}
});
The props.children will be whatever component was designated by React Router.
This way, the actual component for the page can render and take for granted that all the departments are loaded and there is a "current" department already all set up.
The problem is that the component, once it renders, needs to start loading the resources for the department. In order to begin that process, it kicks off an action when it mounts:
componentDidMount: function(){
ResourceActions.getResources();
},
This action will kick off an API call that uses the DeptStore's current Department to know what to load. Then it dispatches a RESOURCE_LOADING event, and, perhaps you can guess, it fails, because:
Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.
That's because basically, the action of the departments loading causes the resource component to render, which tries to kick off a resource fetch action. One action leads to another.
All the examples and similar questions about this type of issue aren't satisfactory to me. For example:
Flux Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch
a scenario flux doesn't support
Even the Todo list and Chat App examples from Facebook do not address this. The Todo list doesn't even have 'initial' data that loads. The Chat App does, but it simply loads all the messages when the app starts.
I cannot load the Resources when the app starts, because I have to know for which department first. Even if I somehow knew that, I do not want to load the Resources unless the Resources component needs them. The user may be on other pages with the current Department, which do not display the Resources. So somehow loading the Resources in response to the original Dept change event is not viable to me either.
Please help me get the insight I'm missing to solve this problem? :) How do I keep the list of Departments separate, and then load Resources in response to the Departments loading, and do it so that it is somehow driven by the component, so that I don't make an expensive Resources API call unless the component on the page needs it?
As the answers in the other posts point out, this is a shortcoming/ hole in the flux pattern solution. The only workaround (similar to previous answers), is to create a call from componentDidMount() and from componentDidUpdate() in the top component, which does something like:
checkAndFetchResources: function() {
if (this.props.resourcesToRender != undefined) {
ResourceActions.getResources();
}
}
And pass the resources fetched as props to the resources component.
That way, your fetch action is called after the previous dispatch round followed by render cycle is completely finished.
This is similar to previously provided answers, so may not be sufficient for you (as you state in your question). In that case, further clarification on WHY the answers do not meet your needs would help.

React vs Angular: Slow rendering with React

I was doing a comparison of Angular and React and decided to try out a performance test to see how fast a large(ish) list would render in both frameworks.
When I got done with my React prototype with some basic currency formating, it was taking ~2 seconds to render on my fast laptop. With Angular it was barely noticeable -- only when I switched to my phone did it have a noticeable lag.
This was very surprising because I was told that React was supposed to beat the pants off of Angular for performance, but it appears that the opposite is true in this case.
I distilled my prototype down to a very simple app to try to isolate the issue: https://github.com/pselden/react-render-test
In this sample, it's taking almost 200ms to render this simple list after changing the language, and I'm barely doing anything.
Am I doing something wrong here?
/** #jsx React.DOM */
'use strict';
var React = require('react'),
Numbers = require('./Numbers');
var numbers = []
for(var i = 0; i < 2000; ++i){
numbers.push(i);
}
var App = React.createClass({
getInitialState: function() {
return { locale: 'en-US' };
},
_onChangeLocale: function(event) {
this.setState({locale: event.target.value});
},
render: function() {
var currentLocale = this.state.locale;
return (
<div>
<select
onChange={this._onChangeLocale}>
<option value="en-US">English</option>
<option value="fr-FR">French</option>
</select>
<Numbers numbers={numbers} locales={[currentLocale]} />
</div>
);
}
});
module.exports = App;
/** #jsx React.DOM */
'use strict';
var React = require('react'),
ReactIntlMixin = require('react-intl');
var Numbers = React.createClass({
mixins: [ReactIntlMixin],
getInitialState: function() {
return {
numbers: this.props.numbers
};
},
render: function() {
var self = this;
var list = this.state.numbers.map(function(number){
return <li key={number}>{number} - {self.formatNumber(number, {style: 'currency', currency: 'USD'})}</li>
});
return <ul>{list}</ul>;
}
});
module.exports = Numbers;
PS: Added an angular version: https://github.com/pselden/angular-render-test
Edit: I opened an issue with react-intl and we investigated and found that there was not that much overhead with using https://github.com/yahoo/react-intl/issues/27 -- it's simply React itself that is slower here.
This is definitely an interesting test case.
If you take a look at the timelines, you can see that Angular is finished handling the change event in a mere 20ms. The remainder of the time is spent in layout and repaint.
React (using a production build, your repo uses dev by default) takes about 59ms. Again, the rest of the time is spent in layout and repaint.
If you take a look at the CPU flame charts, you can see that Angular appears to be doing a lot less work.
Angular:
React:
React provides a pretty good optimization hook in the form of shouldComponentUpdate that is especially useful when a single instance of a component out of thousands should update and the others should stay the same; it's a technique I use in this demo (try it out in an incognito window; I've found some Chrome extensions make layout/repaint times much higher—for me, adding/removing single elements once the list is 1000 long takes ~13ms, changing the size/color of an element takes ~1ms). However, it does no good when every element needs to update.
My guess is that Angular will be faster at changing most or all of the elements in your table, and React will be quite proficient at updating select entries when using shouldComponentUpdate.
I'm surprised nobody mentioned PureRenderMixin. It implements shouldComponentUpdate so you don't have to worry about it.
Also, I wonder if React Performance Tools would turn up something useful?
And I'm surprised to hear Angular is faster than React after watching this talk from Ryan Florence.
We have tried to analyze some of the attributes of our frameworks, of course, this is not the whole list. Below there is a table of consolidated and important, in our opinion, of comparing attributes.
Let’s get some more details:
Although Angular vs React differences are many, they can do the same thing, namely to build client interface. Both have their place. For those peoples who like web development above all interesting is innovative AngularJS approach to HTML.
AngularJS really is a full framework and not just a library, as the ReactJS, but ReactJS has better performance than the AngularJS by implementing virtual DOM. In our opinion, you should use AngularJS if:
you plan to carry a lot of unit tests during development,
you want a comprehensive solution for your application.
However, two-way data binding is often touted advantage of using AngularJS, but because it is implemented through a series digest, adding too much complexity for certain functions and expressions can lead to deterioration in performance of your applications.
In this particular case you need to be aware that the state trickles down and so do the DOM updates. What you want to do is create a Price component that stores the locale in its own state and receives a signal (ie channel or flux) to change the locale as opposed to sending the locale prop all the way down. The idea is that you don't need to update the entire Numbers component, just the prices inside. The component might look like:
var Price = React.createClass({
mixins: [ReactIntlMixin],
componentDidMount: function() {
subscribeToLocal(this.onLocaleChange);
},
onLocaleChange: function(newLocales) {
this.setState({locales: newLocales});
},
render: function() {
return <span>this.formatNumber(this.props.number, this.state.locales, {style: 'currency', currency: 'USD'})</span>
}
});
In React component, once you call setState, it will trigger the render function immediately. React will mark this component as dirty, and will re-render all the children element inside this component.
It will not render the whole Native DOM elements because of the Virtual DOM, thus it will still create new instances of its children ReactElements, which can lead to extra Javascript memory cost.
To avoid this issue, you need shouldComponentUpdate function which implemented in Component Class. it will executed before Render method. If you find the there is nothing changed right now, for instance in your example, you change the state.locale. You can definitely consider this component need no update. so just return false to prevent the render call.
This is a base solution to solve React performance issues. Try to add "shoudlComponentUpdate" in your Numbers Component to avoid tons of element re-render
This is an example where all that is changing is one data output. It's not impossible that Angular's two way data-binding simply offers a faster re-render when all that is changing is the display of the bound data.
React does not promise that its renders are faster than any other framework under all circumstances. What it does offer is the ability to handle ~arbitrarily complex DOM updates in very efficient manner, and offer a variety of lifecycle methods (e.g. componentWillReceiveProps, componentDidUpdate, in addition to the afore-mentioned shouldComponentUpdate) to let you fire callbacks on those events and control how and if they should happen. Here, there's very little to optimize, because all you are doing is changing 2,000 text displays.
edit: To clarify, React is useful in doing more complex DOM manipulations because it's virtual DOM algorithm allows it to choose the minimal set of DOM operations necessary to update your DOM. That's still a lot of work to do when all that needs to be happening is 2000 instances of some text changing.

Resources