redux-logic icon indicating copy to clipboard operation
redux-logic copied to clipboard

Feature Request: waitFor

Open shauntrennery opened this issue 8 years ago • 9 comments
trafficstars

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.

shauntrennery avatar Mar 13 '17 09:03 shauntrennery

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 .

tesla3327 avatar Mar 13 '17 14:03 tesla3327

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.

jeffbski avatar Mar 14 '17 00:03 jeffbski

I have the same problem.

mirandaleandro avatar Mar 15 '17 06:03 mirandaleandro

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);
    }
});

erkiesken avatar May 11 '17 08:05 erkiesken

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().

lstkz avatar May 18 '17 17:05 lstkz

@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.

jeffbski avatar Jun 03 '17 22:06 jeffbski

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)
  }
})

ksloan avatar Dec 12 '17 19:12 ksloan

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.

NoelAramis avatar May 09 '18 19:05 NoelAramis

How are users dealing with this?

mike-niemand avatar Aug 13 '18 10:08 mike-niemand