Question: Multiple dispatches on loader
Opened this issue · 1 comments
Hi All,
I've spent several days on this and cannot get it to work. I have to call multiple different dispatches on a loading screen. I've gone through the examples but dispatch doesn't appear to return a Promise. I have tried removing done and other approaches.
e.g.
Store.dispatch({type: 'GET_STORES'});
Store.dispatch({type: 'GET_CATEGORIES'});
The logic is as follows:
export const storesLogic = createLogic({
type: GET_STORES,
debounce: 500,
latest: true,
warnTimeout: 0,
validate({getState, action }, allow) {
allow(action)
},
async process({ httpClient}, dispatch, done) {
try {
var datalayer = new Datalayer();
let query = '{stores {storeId, storeName, addressStreet1, addressStreet2, suburb, city, cityId, latitude, longitude}}';
await datalayer.postql(query).then(function (stores) {
dispatch(storesFulfilled(stores));
}.bind(this));
} catch(err) {
dispatch(storesRejected(err));
}
done();
}
})
How can I reliably wait for all to complete?
Since logic can run not only once but zero (cancelled) to many times (observable) and even non-ending, there wasn't a nice way to signal being done in all cases.
I typically have my API or data layer be free of redux things, so it could return a promise resolving to the data. Then I do whatever dispatching in logic.
If I have multiple things to do then I would fire them off together from the same logic and wait for both to complete before dispatching the storesFulfilled.
So I would have a LOAD_STORES_AND_CATEGORIES action which kicks off both. I use Promise.all to run then concurrently or just await each if they need to go one after the other.
export const storesLogic = createLogic({
type: LOAD_STORES_AND_CATEGORIES,
debounce: 500,
latest: true,
warnTimeout: 0,
validate({getState, action }, allow) {
allow(action)
},
async process({ API }, dispatch, done) {
try {
// if both should go concurrently use Promise.all, otherwise await each individually
const [stores, categories] = await Promise.all([
API.fetchStores(), // returns a promise to the stores
API.fetchCategories() // returns a promise to the categories
]);
dispatch(storesFulfilled(stores));
dispatch(categoriesFulfilled(categories));
dispatch(storesAndCategoriesLoaded()); // if we need one signaling both are ready
} catch(err) {
dispatch(storesRejected(err));
}
done();
}
})
There are other ways of doing things but this is how I typically do it.
Alternatively if you wanted to keep them separate, you could have each of them check state (using getState) after they are finished dispatching to see you can fire the action signaling both are ready. In this case the last one to finish fires the final action.
so the GET_CATEGORIES logic would resolve and dispatch categoriesFulfilled, then call getState and see if STORES is also populated, then if so dispatch StoresAndCategoriesFulfilled.
and vice versa GET_STORES logic would resolve and dispatch storedFulfilled and then call getState to see if CATEGORIES is populated, then if so dispatch StoresAndCategoriesFulfilled.
The last one to finish would fire the final action.
So that is another way of doing it, though I still prefer the previous way for most use cases.
I am working through some ideas that might make this easier.