Related
Let's take the following scenario where WelcomePage (parent) uses LoginForm (children) with a custom event #submit:
// WelcomePage.vue
<LoginForm #submit="handleLogin">Login<Button>
Then, the component LoginForm has the following code:
// LoginForm
<form #submit.prevent="handleSubmit"> ... </form>
handleSubmit() {
// do some stuff...
// following Vue docs to create custom events.
this.$emit('submit', 'some_data')
// OR... we can also use $listeners.
this.$listeners.submit('some_data')
// do other stuf...
}
Is there any con of using this.$listeners.submit() instead of this.$emit('submit')?
One advantage of using this.$listeners is it can be used with await, which is a limitation of $emit that force us to use the done() callback approach. And it's useful when we wanna update some state (this.isLoading) after the custom event is finished.
Using $emit with callback:
// LoginForm.vue
async handleSubmit() {
this.isLoading = true
this.$emit('submit', 'some_data', () => {
this.isLoading = false
})
}
// WelcomePage.vue
async handleLogin(data, done) {
// await for stuff related to "data"...
done();
}
Using $listeners with await:
// LoginForm.vue
async handleSubmit() {
this.isLoading = true
await this.$listeners.submit('some_data') // no need to use done callback
this.isLoading = false
}
So, my question is: Is it okay to use this.$listeners? What's the purpose / advantage of this.$emit?
UPDATE:
Passing a prop isLoading from the parent to the children would be the first (obvious) option, instead of using $emit.
But that would require to set and update this.isLoading = true | false on handleSubmit every time we use the children component (and it's used a lot). So I'm looking for a solution where the parent doesn't need to worry about isLoading when #submit gets called.
The way I see it is that $emit helps you keep the FLUX architecture.
You can easily see the data flow, it helps you debug.
While using $listeners on the other hand is considered as a bad practice. It can be done but it can break one way data flow. The same is with a $root, you still have access to it, but that doesn't mean you should be using (modifing) it ;-)
Still, what to use as always depends on a context and your need. Just be careful, once broken one way data flow is very hard to debug.
Edit after comment: This is just my point of view on it.
When using props and $emit as recommended way for communication between components. You have a clear data flow. Plus Vue dev-tools helps you track every $emit, so you know exactly what happend step by step.
When using "collbackFunc" as a props and call this callback in a child component that will still work. And that is still a good way to go. The downside of it is that it is not a recommended usage.
Imagin you pass that "callbackFunc" to many childs components. When something goes wrong it is very hard to track from where it was fired.
The same applies to calling directly method on $listeners. Suddenly your state is changed, and you don't know exactly from where. Which and when component has fired it.
Using props instead of callbacks from $emit
const LoginForm = {
name: 'Login-Form',
props: {
loading: {
type: Boolean,
default: false,
}
},
template: `
<button :disabled="loading" #click="$emit('some_data')">
<template v-if="loading">Logging you in</template>
<template v-else>login</template>
</button>
`,
};
new Vue({
el: '#app',
components: {
LoginForm,
},
data() {
return {
loadingLoginForm: false,
};
},
methods: {
handleSubmit() {
// Set loading state before async
this.loadingLoginForm = true;
// Some async shish
setTimeout(() => this.loadingLoginForm = false, 1500)
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<login-Form :loading="loadingLoginForm" #some_data="handleSubmit" />
</div>
I'm still pretty new on React development, but I've already work on 3 big project using React+Redux and I see a pattern that I dislike a lot:
componentWillReceiveProps(nextProps) {
if (nextProps.params.type === TYPE_NEW_USER) {
this.modalUsername = this.props.showPopup( < NewUsernamePopup onClose = {::this.closeUsernamePopup
}
/>, USERNAME_POPUP_ID, true);
}
if (this.state.kind !== nextProps.kind || this.state.filter !== nextProps.filter || this.state.hashtags !== nextProps.hashtags) {
this.setState({
results: [],
loading: true,
kind: nextProps.kind,
filter: nextProps.filter,
hashtags: nextProps.hashtags
}, () => this.manageResults(nextProps.results, false));
} else {
this.manageResults(nextProps.results, true);
}
this.managePages(nextProps.paging);
}
I would like to avoid the ifs inside the componentWillReceiveProps. How do you handle it? We've analysed another project using Flux and callback registration. It looks like:
componentWillMount() {
EntityStore.on(EntityActions.ENTITIES_LOADED, this.getData.bind(this));
EntityActions.entitiesLoaded();
}
The first event is emitted by the component, but afterwards the store emits the event and the component updates. Additionally a single store keeps its state and do not duplicate async calls if it already has the content. I personally like to avoid the ifs, but I do NOT want to lose Redux (its community and tools).
How would you add the current logic (ifs) inside the componentWillReceiveProps outside the component? I would like to handle the logic in a service layer and not inside the component.
I would definitely appreciate to read your opinion around this, because I've been struggling to find a solutions that fits.
The redux approach is to put the logic into the actions/reducers.
So i don't know what your manageResults method does, but it is probably the piece of logic you want to move into a reducer so you won't need to call it from your component anymore.
So the kind,filter and hashtagsvariables should be updated from redux actions only.
tl;dr properly following redux best practices would eliminate some of these conditions, but I'd be more concerned about the overall design this snippet is revealing.
To address the individual lines:
if (nextProps.params.type === TYPE_NEW_USER) {
This looks like a redux action was passed to the component? If so, that's not great, only the reducers should care about action types.
this.modalUsername = this.props.showPopup(
The lifecycle hook componentWillReceiveProps is not the right place to initiate things like that, the resulting React component in an instance var also looks quite weird.
if (this.state.kind !== nextProps.kind || this.state.filter (etc.) ) {
If you have UI state in this component that is somehow dependant on the props coming from redux, these types of ifs are somewhat necessary, since you can't do it outside the component.
You are right to dislike this "pattern", which seems to reflect bad overall design. This component seems to be involved with "pages", "results", a username, and some ajax fetching with a loading flag. Can only speculate of course, but it seems like it's doing too much. The ajax request lifecycle should definitely be modelled in a reducer.
That said, the lifecycle hooks do often contain a bunch of ifs, since the reducers don't see routing and which components get mounted/unmounted, so that's where you have to react to changing props sometimes.
I am looking to create a one page application with ReactJS.
Is it advisable to combine it with angular or it is suitable just on its own? I would like to populate the one page site with sections - adding various features like carousels, sliders, isotope filters ...
<!DOCTYPE html>
<html>
<head>
<title>React Js one page</title>
<script src="https://fb.me/react-with-addons-0.14.7.min.js"></script>
<script src="https://fb.me/react-dom-0.14.7.min.js"></script>
</head>
<body>
<section>
One
<script>
var HelloMessage = React.createClass({
render: function() {
return <div>Hello {this.props.name}</div>;
}
});
ReactDOM.render(<HelloMessage name="Colonel Mustard" />, mountNode);
</script>
</section>
<section>
Two
<script>
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
Hello, world! I am a CommentBox.
</div>
);
}
});
ReactDOM.render(<CommentBox />, mountNode);
</script>
</section>
<section>
Three
<script>
"use strict";
var MarkdownEditor = React.createClass({
displayName: "MarkdownEditor",
getInitialState: function getInitialState() {
return { value: 'Type some *markdown* here!' };
},
handleChange: function handleChange() {
this.setState({ value: this.refs.textarea.value });
},
rawMarkup: function rawMarkup() {
return { __html: marked(this.state.value, { sanitize: true }) };
},
render: function render() {
return React.createElement(
"div",
{ className: "MarkdownEditor" },
React.createElement(
"h3",
null,
"Input"
),
React.createElement("textarea", {
onChange: this.handleChange,
ref: "textarea",
defaultValue: this.state.value }),
React.createElement(
"h3",
null,
"Output"
),
React.createElement("div", {
className: "content",
dangerouslySetInnerHTML: this.rawMarkup()
})
);
}
});
ReactDOM.render(React.createElement(MarkdownEditor, null), mountNode);
</script>
</section>
</body>
</html>
If you're just starting out with React, I'd highly recommend following Pete Hunt's advice:
You’ll know when you need Flux. If you aren’t sure if you need it, you don’t need it.
The best thing to do is get started with React by itself and manage application state using the local state that comes with each of your components.
When you find that you start having to pass data back up to parent components, then add Flux into the mix and rewrite your stateful components to instead use Flux stores.
We'll look at how to tackle writing a simplified version of the StackOverflow answer component as a React application from the bottom up.
Notice, I said React application, not React component. This is because there's no technical difference. A React application is a big React component made up of lots of smaller ones.
Identify Components
Once you have an interface for your application (anything from wireframes to html/css) you can visually subdivide them to work out how they'll fit together as React components.
There are no hard and fast rules about how exactly you decide what should or should not be it's own component, but you'll get a feeling for it the more times you do it.
is <Answer />
is <Votes />
is <AnswerText />
is <AnswerActions />
Because we're building from the bottom up, we'd start by implementing each of the child components and testing that they work alone.
At this point in the development lifecycle we'd just write static markup for each component. There's no need to think about props or state yet.
We can use the stateless component syntax to get started on the components we've identified. Here's an example of how we might write the <Votes /> component.
function Votes() {
return (
<div>
<a>▲</a>
<strong>0</strong>
<a>▼</a>
</div>
);
}
Of course this doesn't do anything, but it allows us to start composing our components to get a feel for the structure of the application.
We can render this into the DOM to check that it works at any time.
ReactDOM.render(<Votes />, document.getElementById('app'));
Once you'd finished implementing static versions of the other components, you could put them together to create the parent <Answer /> component.
function Answer() {
return (
<div>
<Votes />
<AnswerText />
<AnswerActions />
</div>
);
}
Design Data Flow
The next thing to do is to figure out how data flows through your application.
At this point we can create some dummy data in the form of an answer object that looks something like this:
{
"id": 0,
"votes": 0,
"text": "This is an answer"
}
Initially we can render the <Answer /> component by passing this answer object to it as a prop.
<Answer answer={answer} />
Now it's that components job to pass down the appropriate data to its children.
Obviously not each child needs all of the data though, so we'll have to decide what data goes where. Let's update our <Answer /> component.
function Answer(props) {
var answer = props.answer;
return (
<div>
<Votes id={answer.id} count={answer.votes} />
<AnswerText text={answer.text} />
<AnswerActions id={answer.id} />
</div>
);
}
The <Votes /> component needs know the current number of votes and it also needs to know the id of the answer so that it can communicate change to the server.
The <AnswerText /> component just renders a block of text, so that's all we need to pass it.
Finally, the <AnswerActions /> component renders a list of links that allow the user to perform some action (share, edit, flag) on the answer. This component also needs the answer's id so that it can communicate with the server.
Now we have to update these child components in turn to use these new dynamic values, instead of the static values we used at first. We'll revisit the <Votes /> component to see this happen.
function Votes(props) {
var urls = {
upvote: '/api/answers/' + props.id + '/upvote',
downvote: '/api/answers/' + props.id + '/downvote'
};
return (
<div>
<a href={urls.upvote}>▲</a>
<strong>{props.votes}</strong>
<a href={urls.downvote}>▼</a>
</div>
);
}
Now our vote component will make a HTTP request to the appropriate endpoint when we click on the vote buttons, however, we'd rather make this update without reloading and re-rendering the entire application.
Identify Stateful Components
The final piece of the component development process is to identify stateful components. These components have moving parts and data that will change during the lifetime of the application.
Each time the state inside a component changes, the entire component re-renders. We can revisit the wireframes to see which of our components needs to manage changing data.
This application only has one stateful component () and that's `. When we click on one of the arrows, we need to update the number to reflect the new count.
It's the only one of our components that ever needs to re-render.
This means we'll need to upgrade the component to use React's createClass syntax. This allows it to start managing it's own state.
var Votes = React.createClass({
getInitialState: function() {
return { votes: this.props.votes };
},
upvote: function() {
var newVotes = this.state.votes + 1;
this.setState({
votes: newVotes
});
},
downvote: function() {
var newVotes = this.state.votes - 1;
this.setState({
votes: newVotes
});
},
render: function() {
return (
<div>
<a onClick={this.upvote}>▲</a>
<strong>{this.state.votes}</strong>
<a onClick={this.downvote}>▼</a>
</div>
);
}
});
I've jumped the gun a bit and implemented the full component, but hopefully you'll get the idea.
First we use getInitialState to set up some state to represent the initial number of votes in the component.
Next we implement upvote and downvote component methods that update the component state.
Finally we re-implement the render method from before, but have the arrows trigger the new component methods, not page requests.
Each time we make a call to setState, React will re-render the component. Hopefully you can see why we put the state in the <Votes /> component and not the <Answer /> component. It would be crazy to re-render the answer text and actions, just because the votes had changed.
Flux It Up
Once we've identified and implemented all of our stateful components, we can start to move their state out into Flux stores.
It's much more likely that a real application would have an <AnswerStore /> than a <VoteStore />, so that's what we'll implement. For now we'll just keep mocking our data.
var AnswerStore = {
_listeners: [],
_answers: {
"0": {
"id": 0,
"votes": 0,
"text": "This is an answer"
}
},
get: function(id) {
return this._answers[id];
},
update: function(id, update) {
var answer = this.get(id);
var updatedAnswer = Object.assign({}, answer, update);
this._answers[id] = updatedAnswer;
this.emit();
},
listen: function(f) {
this._listeners.push(f);
},
emit: function() {
this._listeners.forEach(function(f) {
f();
});
}
};
In this example, I've written a fairly generic store that contains data, provides simple handlers for listening to model changes and finally exposes methods for mutating the data in the store.
It's important that our update method treats the individual answers as immutable in this application, otherwise we risk mutating an object that other parts of the application have a reference to, causing the object to change underneath them. We use Object.assign to create a new object each time, based on the old one.
The next thing we need to do is set up some action handlers for this store.
dispatcher.register(function(action) {
switch(action.type) {
case 'UPVOTE':
var votes = ActionStore.get(action.id);
ActionStore.update(action.id, { votes: votes + 1 });
break;
case 'DOWNVOTE':
var votes = ActionStore.get(action.id);
ActionStore.update(action.id, { votes: votes - 1 });
break;
}
});
This simply wires the update method to two actions called 'UPVOTE' and 'DOWNVOTE'
Now we connect Flux to our <AnswerComponent /> which needs to be re-written in the long form.
var Answer = React.createClass({
getInitialState: function() {
return { answer: {} };
},
componentWillMount: function() {
this.update();
AnswerStore.listen(this.update);
},
update: function() {
var id = this.props.id;
this.setState({ answer: AnswerStore.get(id) });
},
render: function() {
var answer = this.state.answer;
return (
<div>
<Votes id={answer.id} count={answer.votes} />
<AnswerText text={answer.text} />
<AnswerActions id={answer.id} />
</div>
);
}
});
In our componentWillMount method we fetch our initial data for the store, then set up a listener on the store that fetches and updates the component state, whenever the store changes.
Finally, we need a way to dispatch the appropriate actions from our <Votes /> component.
The most popular way to do this is with action creators. An action creator is a function which takes some data as parameters, then packages it up and dispatches it as an action.
var Actions = {
upvote: function(id) {
dispatcher.dispatch({
type: 'UPVOTE',
id: id
});
},
downvote: function(id) {
dispatcher.dispatch({
type: 'DOWNVOTE',
id: id
});
}
};
Then we call these actions from inside our <Votes /> component (which can become stateless again).
function Votes(props) {
var id = props.id;
return (
<div>
<a onClick={Actions.upvote.bind(null, id)}>▲</a>
<strong>{props.votes}</strong>
<a onClick={Actions.downvote.bind(null, id)}>▼</a>
</div>
);
}
This component now uses the action creators to dispatch actions for our Flux store(s) to handle.
If we look at the flow of data through our application, we can see that we now have a unidirectional cycle, rather than a tree.
The <Answer /> component passes the id down to the <Votes /> component.
The <Votes /> component dispatches an action using that id.
The AnswerStore processes the action and emits a change.
The <Answer /> component hears the update and updates its state, re-rendering its children.
Here's a jsfiddle of this demo application.
Scale Up
This is a very simple component that only handles a tiny amount of data flow and even less application state, however, it's enough to show you how to compose a React component and that's all you need to build a React app.
Let's imagine we were implementing a StackOverflow question as a React application.
function App() {
return (
<Page>
<Navigation />
<SideBar>
<MetaDetails />
<Ads />
<RelatedQuestions />
</SideBar>
<Question />
<AnswerList />
<AnswerEditor />
<Footer />
</Page>
);
}
It might seem like a complex application, but you can break it down and express it as distinct components, then you can implement and test the components individually, just like we did here and bring them altogether to form a complex application.
Don't Over Complicate
For most simple React applications like this one, Flux is not actually necessary. It's worth remembering that React was released over a year before Flux and yet it was adopted by a lot of developers regardless.
Of course, I've only really covered structuring and implementing components here. Taking an application from wireframes to deployment is a much more complicated process and there's no way it could be covered in detail in one answer. In time, you'll probably also want to learn about:
Package management and dependencies
Module bundlers
Routing
Hot Reloading
ES6/Babel
Redux
Server Side Rendering
Immutable Data
Inline Styles
Relay/Falcor/GraphQL
It takes some time to get through this list of things, the trick is not to rush. Don't overcomplicate your existing project until you find the reasons that these solutions exist, naturally.
I think react-howto is the best guide out there. Although it's not heavy on detail, it links to a lot of good resources and most importantly it provides an opinionated guide to the order in which you should learn these technologies on your way to becoming a competent ReactJS developer.
The choice of framework (Angular/React) does not prevent building any of the functionality you described, and your site could be built with neither, either, or both of those frameworks.
While you certainly could combine Angular and React, I'm not sure why you would want to, and it's not going to be the easiest of tasks. It's doable, of course, but it's going to provide a lot of difficulty for very little ultimate gain.
If you want to build a SPA with React, I would focus more on finding a Flux implementation that you like, and learning how to integrate that. Flux is designed specifically with React in mind to handle large SPAs which might have complicated data flow, and it's certainly a tool that is easier to incorporate early on.
The only other library I would consider out of the gate would be Immutable.js, which pairs very well with both React and Flux.
But otherwise, until you find a need to bring in additional frameworks/libraries, attempt to hold off. With all of the exciting JS frameworks out there, it's tempting to want to use them all, but in reality you're better off picking one to focus on, and then maybe bringing in some tools from another later on when they are necessitated.
I am currently learning react and I have run into the problem of elegently extracting states from my components.
basically I have a few components which contains forms and other inputs from which I need the data in my business logic, the data I need is coupled with the state of the component. From what I understand the data should have unidirectional flow but I can't think of how I can make my data flow back towards my business logic. I could just make some interface functions which call the respective, but I feel this would violate the unidirectional flow.
anyhelp with some examples would be greatly appreciated!
You typically pass down callbacks from parent components to child components as props. When the state changes in any of the child components, it invokes that callback and passes whatever data is appropriate in each use case. Your "controller-view" (the root component that implements the actual callback) then does whatever business logic you need based on the current state and then updates its state accordingly (causing a re-render of the component tree from that component down). Read the docs about component communication.
Something like this:
var Child = React.createClass({
onTextChange: function() {
var val = 13; // somehow calculate new value
this.props.onTextChange(val);
},
render: function() {
return <input type="text" value={this.props.val} onChange={this.onTextChange} />
}
});
var Parent = React.createClass({
onTextChange: function(val) {
var newVal = someBusinessLogic(val);
this.setState({val: newVal});
},
render: function() {
return <Child onTextChange={this.onTextChange} val={this.state.val} />
}
});
The best way to work with data flow in React is to use the Flux pattern. You need some time to understand how it works, but it will make your life much easier as your project grows.
Look at some Flux tutorial, for example this one (using the Alt flux implementation): https://reactjsnews.com/getting-started-with-flux/
I'm rewriting my app to use Flux and I have an issue with retrieving data from Stores. I have a lot of components, and they nest a lot. Some of them are large (Article), some are small and simple (UserAvatar, UserLink).
I've been struggling with where in component hierarchy I should read data from Stores.
I tried two extreme approaches, neither of which I quite liked:
All entity components read their own data
Each component that needs some data from Store receives just entity ID and retrieves entity on its own.
For example, Article is passed articleId, UserAvatar and UserLink are passed userId.
This approach has several significant downsides (discussed under code sample).
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>
)
}
});
Downsides of this approach:
It's frustrating to have 100s components potentially subscribing to Stores;
It's hard to keep track of how data is updated and in what order because each component retrieves its data independently;
Even though you might already have an entity in state, you are forced to pass its ID to children, who will retrieve it again (or else break the consistency).
All data is read once at the top level and passed down to components
When I was tired of tracking down bugs, I tried to put all data retrieving at the top level. This, however, proved impossible because for some entities I have several levels of nesting.
For example:
A Category contains UserAvatars of people who contribute to that category;
An Article may have several Categorys.
Therefore if I wanted to retrieve all data from Stores at the level of an Article, I would need to:
Retrieve article from ArticleStore;
Retrieve all article's categories from CategoryStore;
Separately retrieve each category's contributors from UserStore;
Somehow pass all that data down to components.
Even more frustratingly, whenever I need a deeply nested entity, I would need to add code to each level of nesting to additionally pass it down.
Summing Up
Both approaches seem flawed. How do I solve this problem most elegantly?
My objectives:
Stores shouldn't have an insane number of subscribers. It's stupid for each UserLink to listen to UserStore if parent components already do that.
If parent component has retrieved some object from store (e.g. user), I don't want any nested components to have to fetch it again. I should be able to pass it via props.
I shouldn't have to fetch all entities (including relationships) at the top level because it would complicate adding or removing relationships. I don't want to introduce new props at all nesting levels each time a nested entity gets a new relationship (e.g. category gets a curator).
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-L91
http://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.
The problems described in your 2 philosophies are common to any single page application.
They are discussed briefly in this video: https://www.youtube.com/watch?v=IrgHurBjQbg and Relay ( https://facebook.github.io/relay ) was developed by Facebook to overcome the tradeoff that you describe.
Relay's approach is very data centric. It is an answer to the question "How do I get just the needed data for each components in this view in one query to the server?" And at the same time Relay makes sure that you have little coupling across the code when a component used in multiple views.
If Relay is not an option, "All entity components read their own data" seems a better approach to me for the situation you describe.
I think the misconception in Flux is what a store is. The concept of store exist no to be the place where a model or a collection of objects are kept. Stores are temporary places where your application put the data before the view is rendered. The real reason they exist is to solve the problem of dependencies across the data that goes in different stores.
What Flux is not specifying is how a store relate to the concept of models and collection of objects (a la Backbone).
In that sense some people are actually making a flux store a place where to put collection of objects of a specific type that is not flush for the whole time the user keeps the browser open but, as I understand flux, that is not what a store is supposed to be.
The solution is to have another layer where you where the entities necessary to render your view (and potentially more) are stored and kept updated. If you this layer that abstract models and collections, it is not a problem if you the subcomponents have to query again to get their own data.