Search code examples
reactjsrefluxjs

ReactJS render() method called more often than necessary in complex RefluxJS application


I have a RefluxJS application which has several stores and a pretty deep component hierarchy. I've tried to make the components very independent, with each connecting to the stores it needs to render; and the stores themselves sometimes call Actions that other stores listen to.

I've found that I get a lot of spurious calls to my components' render() methods, because two stores might listen to the same action, and different components in the hierarchy might listen to those different stores. This is affecting the user experience because sometimes there's a little bit of lag.

Here's some code:

var Actions = Reflux.createActions(['changeUser']);
Actions.changeUser.preEmit = () => console.log('Action emit: changeUser');

var UserStore = Reflux.createStore({
  listenables: [Actions],

  onChangeUser(user) {
    this.trigger(user);
  }
});

var MessageStore = Reflux.createStore({
  listenables: [Actions],

  onChangeUser(user) {
    setTimeout(() => {
      // pretend we had to go to an API or something to get this
      var message = "Welcome to the app!";
      this.trigger(message);
    }, 500);
  },
});

var App = React.createClass({
  mixins: [Reflux.connect(UserStore, 'user')],

  render() {
    console.log('Component render: App');

    if (!this.state.user) {
      return (
        <button onClick={() => Actions.changeUser('Some User')}>
          Click to make stuff happen
        </button>
      );
    }

    return (
      <div>
        <div>Hello, {this.state.user}.</div>
        <Message / >
      </div>
    );
  }
});

var Message = React.createClass({
  mixins: [Reflux.connect(MessageStore, 'message')],

  render() {
    console.log('Component render: Message');

    return <div>Your message: {this.state.message}</div>;
  }
});

ReactDOM.render( <App/> , document.getElementById('app'));

Fiddle: https://jsfiddle.net/9xwnxos6/

This is way oversimplified (in this example I'd probably just add a loading indicator of some kind), but illustrates the basic issue that you can see the UI sort of transitioning through intermediate states while you're doing things.

How can I improve my React/Reflux design in order to avoid multiple renders triggering from a single interaction?


Solution

  • It seems my issues are coming from violating a couple of good React/Flux practices:

    1. Don't trigger Actions from within a Store
    2. Only connect to Stores in high-level components

    I am refactoring to get the Store connections out of the low-level components. I'm not crazy about this, because it means I have to pass all kinds of props through these deep component hierarchies to get them where they need to be. But it seems that is the "right" way to build a Reflux app.

    If no one else posts an answer within the next few days, I will accept this one.