I am rewriting my application to use Flux, and I have a problem retrieving data from Stores. I have many components and they nest a lot. Some of them are large ( Article ), some are small and simple ( UserAvatar , UserLink ).
I struggled with where in the component hierarchy I should read data from stores.
I tried two extreme approaches, none of which I liked:
All entity components read their own data.
Each component that needs some data from the Store receives only the object identifier and receives the object itself.
For example, Article is passed to articleId , UserAvatar and UserLink are passed to userId .
This approach has several significant drawbacks (discussed in the sample code).
var Article = React.createClass({ mixins: [createStoreMixin(ArticleStore)], propTypes: { articleId: PropTypes.number.isRequired }, getStateFromStores() { return { article: ArticleStore.get(this.props.articleId); } }, render() { var article = this.state.article, userId = article.userId; return ( <div> <UserLink userId={userId}> <UserAvatar userId={userId} /> </UserLink> <h1>{article.title}</h1> <p>{article.text}</p> <p>Read more by <UserLink userId={userId} />.</p> </div> ) } }); var UserAvatar = React.createClass({ mixins: [createStoreMixin(UserStore)], propTypes: { userId: PropTypes.number.isRequired }, getStateFromStores() { return { user: UserStore.get(this.props.userId); } }, render() { var user = this.state.user; return ( <img src={user.thumbnailUrl} /> ) } }); var UserLink = React.createClass({ mixins: [createStoreMixin(UserStore)], propTypes: { userId: PropTypes.number.isRequired }, getStateFromStores() { return { user: UserStore.get(this.props.userId); } }, render() { var user = this.state.user; return ( <Link to='user' params={{ userId: this.props.userId }}> {this.props.children || user.name} </Link> ) } });
The disadvantages of this approach are:
- This frustrates the availability of 100s components that could potentially be subscribed to stores;
- It is difficult to track how the data is updated and in what order , because each component independently extracts its data;
- Even if you already have an entity in a state, you are forced to pass on your identifier to children who will receive it again (or violate consistency).
All data is read once at the top level and passed to the components
When I was tired of tracking errors, I tried to put all the data at the top level. However, this turned out to be impossible, because for some objects I have several levels of nesting.
For example:
- A
Category contains UserAvatar people who contribute to this category; Article may have several Category s.
Therefore, if I wanted to get all the data from Stores at the Article level, I would need:
- Get an article from
ArticleStore ; - Retrieve all article categories from
CategoryStore ; - Separately, extract contributors from each category from the
UserStore ; - Somehow pass all this data to the components.
Even more disappointing, when I need a deeply nested entity, I will need to add code for each level of nesting to further convey it.
Summarizing
Both approaches seem erroneous. How can I solve this problem most elegantly?
My goals:
Stores should not have an insane number of subscribers. It is stupid for every UserLink to listen to the UserStore if the parent components already do this.
If the parent component retrieved some object from the repository (for example, user ), I do not want the nested components to come to it again. I would have to pass it through the props.
I will not need to get all entities (including relationships) at the top level, because this will complicate the addition or removal of relationships. I do not want to introduce new details at all levels of nesting every time a nested object receives new relationships (for example, a category receives a curator ).
javascript reactjs reactjs-flux
Dan Abramov Sep 06 '14 at 14:16 2014-09-06 14:16
source share