Dan February 2016

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.

Answers


Josh Beam February 2016

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.


Dan Abramov February 2016

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.

Post Status

Asked in February 2016
Viewed 3,868 times
Voted 11
Answered 2 times

Search




Leave an answer