It’s unclear to me how to coordinate multiple, sequential actions in Redux. For example, I’ve got an app that has a summary panel on the left and a CRUD panel on the right. After the user CRUDs, I want the app to automatically update the summary. I also want the ability to refresh the summary and CRUD independently of each other as well. In other words, I don’t want the CRUD actions to have to know about the summary or any other future downstream follow-on actions (or vice versa). Is there a best practice to coordinate this without having my action creators know about other actions?
For example, here’s the (thunk) action creator for clearing a collection of entities (a CRUD action). Right, now it tightly couples the fetchSummary()
dispatch.
export function clearCollection(collection) {
return function(dispatch) {
dispatch(requestCollectionClear(collection));
return doClearCollection(collection)
.then(function(coll) {
dispatch(receiveCollectionCleared(coll))
})
.then(function() {
dispatch(fetchSummary()); // <-- How do I chain actions without tight coupling?
});
// TODO: .catch()
}
}
where requestCollectionClear()
kicks off the async action and fetchSummary()
is the action creator for the subsequent step in the workflow. What’s the best pattern to separate fetchSummary()
from clearCollection()
, decoupling them for use independent of each other?
If I'm understanding your answer to my question in the comment correctly (that your CRUD ops update some collection data and your summary displays some version of the same collection data), I think you can obviate your problem by thinking about your application differently.
Your state should be something along the lines of an object with a collections
key and your reducers should operate on this object. So a clearCollections
method would set the collections
value to an empty array, a fetchCollections
method would set the collections
value to a fresh array of data from the server, etc.
Then, your summary would just subscribe to your store
and display the latest and greatest value for the collections
key. No more tight coupling.
EDIT: Based on the comments I think you are basically asking how to coordinate between your server and client with regard to your data. One way of handling this is to fetch your collection data from the server on app load, then populate your Redux store with this data by dispatching an action (and passing your data to the action creator):
// Fetch your data
$.ajax({
url: '/collections',
method: 'GET',
success: function(response) {
// Populate your Redux store by dispatching an action
store.dispatch(setCollections(response));
}
});
Then, when you want to update your data, say add a collection, you would do something like this:
// Update the Redux store, which updates the UI.
store.dispatch(addCollection(someNewData));
Then update the server, and reconcile on response
$.ajax({
url: '/collections',
method: 'POST',
data: {collection: someCollection},
success: function(response) {
// Should be a no-op b/c client has already
// been updated.
store.dispatch(setCollections(response))
}
});
Your action creators and reducers would look like this:
// Action creators:
function setCollections(collections) {
return {type: 'setCollections', collections: collections}
}
function addCollection(collection) {
return {type: 'addCollection', collection: collection}
}
// Reducer
function reduceCollections(state = initialState, action) {
if (action.type === 'setCollections') {
return {collections: action.collections}
} else if (action.type === 'addCollection') {
return {
collections: state.collections.concat([action.collection])
}
} else {
return state
}
}
How you handle all this is up to you. I've tried to keep it simple above to illustrate the point but you could use thunks for the async calls and switch
statements or object maps for the reducers if you prefer.
Approaching this with fresh eyes and more experience after three years, I’d definitely externalize the state transitions, using something like XState. The state machine would keep track of the transitions and Redux would maintain the backing data, dispatching update events to components.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With