Reputation: 59336
I'm studying Flux and I think I understood the workflow:
View -> Action -> Dispatcher -> Store -> View
However, I didn't quite understand where am I supposed to populate the initial state of my Stores.
For instance, let's say that I'm editing a Contact. So I'd assume I'd have a ContactsStore
. This is what I imagine would happen when I access the URL /contacts/edit/23
:
ContactsStore
gets populated with the contact I'm editing, in this case, contact 23. The data would come from the server.EditContact
view would receive a notification from the ContactsStore
, so it would render itself in the initial state.SaveContact
action and the flow would go on.Step (1) is not clear to me. Where is the ContactsStore
expected to be populated with the initial state? Where do I call the server? Is it on the Store?
Thanks.
Upvotes: 5
Views: 504
Reputation: 10742
When you're constructing the initial state of your app, you should fire an action to fetch the data for "contact 23". That action makes an asynchronous call, which results in an event, which populates a store, which a component uses to get its state.
However, do you want to put the logic that fires the action in that component? Not necessarily. Are you using any routing libraries? If so, they are likely the best place from which to trigger the action.
For example, using fluxible-router, its routing configuration allows you to specify that each route (e.g. /contacts/23
) should fire an action.
This lets you decouple how to display data from how to retrieve it. You could use the same component and have it get its data from AJAX in one case, from local storage in another case, etc. You could also optimize the fetching of data, e.g. by bundling together multiple calls, without having to change any components.
Upvotes: 0
Reputation: 585
I agree with Florian Gl answer though i would recommend reading the article below, about higher order components, you should leave the logic out of your component, and use a higher order component that passed the data as props, avoid using state as much as possible , it will only add extra complexity
in Your higherlevel component(container): componentWillMount handler you can fire an action which does the api request, on success this state is saved to the store, and as soon as the store receives the contact it fires an event on which the EditContact component Container has subscribed -> which is passed to the editContact component
State should live in your store:)
https://medium.com/@dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
Upvotes: 1
Reputation: 1802
The way I, and I think many others, do it is I trigger an action from the view to load the contact. Let's call it LOAD_CONTACT
. This would be an asynchronous action. Some people put the API call in the store directly, but I think it's more common to do async work in the action creators. So let's say you have an action creator loadContactAction()
. Then that would first dispatch the LOAD_CONTACT
action (just in case some store might be interested in this, for displaying a "loading" message or something), then fetch from the API. In the callback of the ajax request, you call another action creator, e.g. loadContactSuccessAction()
(or "failed", of course). Your ContactsStore
store then registers and reacts to LOAD_CONTACT_SUCCESSFUL
.
var loadContactAction = function(...) {
// maybe do some work
dispatch(...); // dispatch your LOAD_CONTACT action
makeRequest(...).then(function(contact) {
loadContactSuccessAction(contact); // create "success" action
}, function(err) {
loadContactFailedAction(err); // probably handle this somewhere
});
}
var ContactsStore = {
init(...) {
// register in dispatcher here
},
onLoadContactSuccess(contact) {
this.contacts[contact.id] = contact; // or store your contact some other way
this.emitChange(); // trigger a store update change event with whatever event dispatcher you use
}
}
var EditContact = React.createClass({
componentDidMount: function() {
ContactsStore.listen(this.onStoreChange);
loadContactAction(); // pass in ID or however you want to load it
},
onStoreChange: function() {
// fetch your contact here
},
render: function() {
...
}
});
Upvotes: 3
Reputation: 6014
You need to have access to the action and the store in your EditContact
component. In the componentDidMount
handler you could fire an action which does the api request. On success it passes the contact to the dispatcher
/store
. As soon as the store receives the contact
it fires an event on which the EditContact
component has subscribed. In the corresponding handler the component sets the new state with the new contact. I'm sure there are other ways to do this, but that's how I would do it.
Upvotes: 5