redux-logic
redux-logic copied to clipboard
Feature Request: waitFor
Enhance the validate block with a waitFor option.
An example:
During init, an application validates a user's session and populates auth state. Should a redux-logic function depend on auth state but execute prior to auth processes being processed, the validate block can delay execution till a known action is received.
I also think this could be useful. I have done things like this before. Like building my API interface to queue requests until the user has been authenticated.
However, this allows there to be a lot of dependencies in your logic. Are there best practices around doing this sort of thing?
On Mon, Mar 13, 2017 at 5:44 AM Shaun Trennery [email protected] wrote:
Enhance the validate block with a waitFor option.
An example:
During init, an application validates a user's session and populates auth state. Should a redux-logic function depend on auth state but execute prior to auth processes being processed, the validate block can delay execution till a known action is received.
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/jeffbski/redux-logic/issues/42, or mute the thread https://github.com/notifications/unsubscribe-auth/AC75LsyN9jKYJe0jyzDxMdCHLyiyZxVOks5rlRAGgaJpZM4MbCBb .
Thanks for posting the request. I'll give it some thought. I'm currently at React Conf, so it will probably be a couple days before I can get a good block of time.
I have the same problem.
Just so Jeff has more examples of use-cases this is how I've handled chaining and waiting for other logics to finish.
This example is extracted from an e-commerce app that on direct "buy" page landing needs to prepare some data (fetch user and product) before it can do other actions. So buy page calls BUY_PREPARE on mounting and that internally delegates to other logics, waits for them to finish and then does its job in onComplete handler.
The pattern is to create and pass notify signal channels in action.meta, and the logics in question then need to tell this notify signal when they are done or if there were errors.
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import "rxjs/add/observable/forkJoin"
import "rxjs/add/operator/defaultIfEmpty";
import "rxjs/add/operator/finally";
export const buyPagePrepareLogic = createLogic({
type: BUY_PREPARE,
process({ getState, action }, dispatch, done) {
const onComplete = () => {
// safe to use getState here, user and products are loaded
const user = getState().user;
const products = getState().producs;
// ... continue with removing loader screen etc
};
const onError = (err) => {
// logging etc
};
// defaultIfEmpty guards against empty complete stream
// from short-circuiting forkJoin below
const userSignal = new Subject().defaultIfEmpty(true);
const productSignal = new Subject().defaultIfEmpty(true);
Observable
.forkJoin([userSignal, productSignal])
.finally(done)
.subscribe(null, onError, onComplete);
// null payload in this case but set action.meta.notify
dispatch(FETCH_USER(null, { notify: userSignal }));
dispatch(FETCH_PRODUCTS(null, { notify: productSignal }));
}
});
export productFetchLogic = createLogic({
type: FETCH_PRODUCTS,
process({ action }, dispatch, done) {
const { notify } = action.meta;
api
.getPrducts()
.then((res) => {
dispatch(PRODUCTS_LOADED(res.products));
notify.complete();
})
.catch((err) => {
// log error and..
notify.error(err);
})
.then(done);
}
});
export userFetchLogic = createLogic({
type: FETCH_USER,
process({ action }, dispatch, done) {
const { notify } = action.meta;
api
.getUserProfile()
.then((res) => {
dispatch(USER_LOADED(res.user));
notify.complete();
})
.catch((err) => {
// log error and..
notify.error(err);
})
.then(done);
}
});
This is related to #57
@tehnomaag
The perfect solution would be to add await to dispatch
dispatch(FETCH_USER(null, { notify: userSignal }));
dispatch(FETCH_PRODUCTS(null, { notify: productSignal }));
->
await Promise.all([
dispatch(FETCH_USER()),
dispatch(FETCH_PRODUCTS()),
]);
The promise can be resolved if all logics call done().
@Thanks @tehnomaag and @lsentkiewicz.
I agree that async / await is compelling for many cases. I wonder what we would want to do in cases of cancellation?
Alternatively I could return an observable. It is easy to go from that to a promise. Or maybe there is a new processOption that allows you to specify which you want.
Good ideas to ponder.
Is it a bad idea to pass a followup action into dispatch? For example:
dispatch({
type: FIRST_ACTION,
nextAction: {
type: FOLLOWUP_ACTION
}
})
Then in the firstActionLogic, calling dispatch(action.nextAction)
export firstActionLogic = createLogic({
type: FIRST_ACTION,
process({ action }, dispatch, done) {
doAsync
.then((res) => {
dispatch(action.nextAction)
})
.then(done)
}
})
Is this getting implemented at any point? It's been more than a year since this feature has been requested. Lack of waitFor operation is one of the biggest drawbacks of redux-logic, and one of the reasons why I'm still using thunks.
How are users dealing with this?