At what nesting level should components read entities from Stores in Flux? At what nesting level should components read entities from Stores in Flux? reactjs reactjs

At what nesting level should components read entities from Stores in Flux?


Most people start out by listening to the relevant stores in a controller-view component near the top of the hierarchy.

Later, when it seems like a lot of irrelevant props are getting passed down through the hierarchy to some deeply nested component, some people will decided it's a good idea to let a deeper component listen for changes in the stores. This offers a better encapsulation of the problem domain that this deeper branch of the component tree is about. There are good arguments to be made for doing this judiciously.

However, I prefer to always listen at the top and simply pass down all the data. I will sometimes even take the entire state of the store and pass it down through the hierarchy as a single object, and I will do this for multiple stores. So I would have a prop for the ArticleStore's state, and another for the UserStore's state, etc. I find that avoiding deeply nested controller-views maintains a singular entry point for the data, and unifies the data flow. Otherwise, I have multiple sources of data, and this can become difficult to debug.

Type checking is more difficult with this strategy, but you can set up a "shape", or type template, for the large-object-as-prop with React's PropTypes. See: https://github.com/facebook/react/blob/master/src/core/ReactPropTypes.js#L76-L91http://facebook.github.io/react/docs/reusable-components.html#prop-validation

Note that you may want to put the logic of associating data between stores in the stores themselves. So your ArticleStore might waitFor() the UserStore, and include the relevant Users with every Article record it provides through getArticles(). Doing this in your views sounds like pushing logic into the view layer, which is a practice you should avoid whenever possible.

You might also be tempted to use transferPropsTo(), and many people like doing this, but I prefer to keep everything explicit for readability and thus maintainability.

FWIW, my understanding is that David Nolen takes a similar approach with his Om framework (which is somewhat Flux-compatible) with a single entry point of data on the root node -- the equivalent in Flux would be to only have one controller-view listening to all stores. This is made efficient by using shouldComponentUpdate() and immutable data structures that can be compared by reference, with ===. For immutable data structures, checkout David's mori or Facebook's immutable-js. My limited knowledge of Om primarily comes from The Future of JavaScript MVC Frameworks


The approach at which I arrived is having each components receive its data (not IDs) as a prop. If some nested component needs a related entity, it's up to the parent component to retrieve it.

In our example, Article should have an article prop which is an object (presumably retrieved by ArticleList or ArticlePage).

Because Article also wants to render UserLink and UserAvatar for article's author, it will subscribe to UserStore and keep author: UserStore.get(article.authorId) in its state. It will then render UserLink and UserAvatar with this this.state.author. If they wish to pass it down further, they can. No child components will need to retrieve this user again.

To reiterate:

  • No component ever receives ID as a prop; all components receive their respective objects.
  • If child components needs an entity, it's parent's responsibility to retrieve it and pass as a prop.

This solves my problem quite nicely. Code example rewritten to use this approach:

var Article = React.createClass({  mixins: [createStoreMixin(UserStore)],  propTypes: {    article: PropTypes.object.isRequired  },  getStateFromStores() {    return {      author: UserStore.get(this.props.article.authorId);    }  },  render() {    var article = this.props.article,        author = this.state.author;    return (      <div>        <UserLink user={author}>          <UserAvatar user={author} />        </UserLink>        <h1>{article.title}</h1>        <p>{article.text}</p>        <p>Read more by <UserLink user={author} />.</p>      </div>    )  }});var UserAvatar = React.createClass({  propTypes: {    user: PropTypes.object.isRequired  },  render() {    var user = this.props.user;    return (      <img src={user.thumbnailUrl} />    )  }});var UserLink = React.createClass({  propTypes: {    user: PropTypes.object.isRequired  },  render() {    var user = this.props.user;    return (      <Link to='user' params={{ userId: this.props.user.id }}>        {this.props.children || user.name}      </Link>    )  }});

This keeps innermost components stupid but doesn't force us to complicate the hell out of top level components.


My solution is much simpler. Every component that has its own state is allowed to talk and listen to stores. These are very controller-like components. Deeper nested components that don't maintain state but just render stuff aren't allowed. They only receive props for pure rendering, very view-like.

This way everything flows from stateful components into stateless components. Keeping the statefuls count low.

In your case, Article would be stateful and therefore talks to the stores and UserLink etc. would only render so it would receive article.user as prop.