I've been struggling to come up with a good pattern for handling loading state in Flux (specifically using Fluxxor, though I think this is an issue for any implementation).
When I say "loading state," what I mean is state in a store that tracks:
- Whether the data handled by the store was loaded
- Whether the store is currently attempting to load data
- Whether the data loaded successfully or errored
- The error message, if it errored
Here's my first (very simple) pass at this, a store mixin called LoadingStoreMixin.js
:
module.exports = {
// Call in the constructor and any time the data is to be "reset"
initLoadingState: function() {
this.loaded = false;
this.loading = false;
this.didError = false;
this.error = null;
},
// Call when loading is begun (e.g. onLoadStart)
startLoading: function() {
this.loaded = false;
this.loading = true;
this.didError = false;
this.error = null;
},
// Call when loading succeeds (e.g. onLoadSuccess)
finishLoading: function() {
this.loaded = true;
this.loading = false;
this.didError = false;
this.error = null;
},
// Call when loading fails (e.g. onLoadErrror)
errorLoading: function(err) {
this.loaded = false;
this.loading = false;
this.didError = true;
this.error = err;
}
};
(Fluxxor doesn't yet support mixins out of the box, but it's easy enough to fudge with e.g. _.merge
on the hash passed to Fluxxor.createStore
).
The intended usage is to manually call these functions from within your load messages:
var ContactsActions = {
load: function() {
this.dispatch('contacts:load');
someAjax({
url: '/contacts',
method: 'GET'
}).then((resp) => {
this.dispatch('contacts:loadSuccess', resp);
}, (err) => {
this.dispatch('contacts:loadError', err);
});
}
};
var ContactsStore = Fluxxor.createStore(_.merge({
initialize: function() {
this.contracts = null;
this.initLoadingState();
this.bindActions(
'contacts:load', 'onLoadContacts',
'contacts:loadSuccess', 'onLoadContactsSuccess',
'contacts:loadError', 'onLoadContactsError'
);
},
onLoadContacts: function() {
this.startLoading();
this.emit('change');
},
onLoadContactsSuccess: function(resp) {
this.finishLoading();
this.contacts = resp.data;
this.emit('change');
},
onLoadContactsError: function(err) {
this.errorLoading(err);
this.emit('change');
}
}, LoadingStateMixin);
Then, your component can use the loading
, loaded
, didError
, and error
attributes as expected.
There's a lot of limitations this mixin has (for example: it can't really handle >1 loadable resource at a time :<), but I feel like it's better than nothing. Ideally, Flux implementations like Fluxxor will start including more robust versions of patterns like this to ease some of the initial pains of boilerplate, as well as to provide best practices for storing data and state.
Interesting, this looks cool. I like the idea.