Crocsx
Crocsx

Reputation: 7609

RXJS make an observable dispatch after another (not concat)

I am using redux, redux-observable in react.

I have the following

const goToItemEpic = (action$, state$): Observable<any> => action$.pipe(
    ofType(ItemsDetailsActions.goToItem),
    concatMap((action: {payload: {projectId: string; datastoreId: string; itemId: string}}) =>
        of(ProjectActions.setCurrentProjectId({projectId: action.payload.projectId})).pipe(
            delay(1000),
            switchMap(() => {
                return [
                    DatastoreActions.setCurrentDatastoreId({datastoreId: action.payload.datastoreId}),
                    ItemsActions.setCurrentItemId({ itemId: action.payload.itemId })
                ];
            })
        )
    )
);

Basically, I would like to dispatch setCurrentProjectId then wait for some time, and then dispatch setCurrentDatastoreId and setCurrentItemId

I face two problems :

1) The current code delay setCurrentDatastoreId and setCurrentItemId but do not execute setCurrentProjectId, why ?

2) Here the delay is an hardcoded 1000ms. but I would like to delay based on another observable execution getDatastoresSuccess

so the it would be

execute => setCurrentProjectId
waitFor => getDatastoresSuccess
execute => setCurrentDatastoreId
execute => setCurrentItemId

How do I manage to do that in redux-observable?

EDIT :

const goToItemEpic = (action$, state$): Observable<any> => action$.pipe(
    ofType(ItemsDetailsActions.goToItem),
    concatMap((action: {payload: {projectId: string; datastoreId: string; itemId: string}}) =>
        concat(
            ProjectActions.setCurrentProjectId({projectId: action.payload.projectId}),
            action$.pipe(
                ofType(DatastoreActions.getDatastoresSuccess),
                first(),
                switchMap(() => of(
                    DatastoreActions.setCurrentDatastoreId({datastoreId: action.payload.datastoreId}),
                    ItemsActions.setCurrentItemId({ itemId: action.payload.itemId })
                ))
            )
        )
    )
);

subscribeTo.js:23 Uncaught TypeError: You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

Upvotes: 1

Views: 428

Answers (1)

Harald Gliebe
Harald Gliebe

Reputation: 7564

You can do that by using action$.pipe again to wait for the required action:

const goToItemEpic = (action$, state$): Observable<any> => action$.pipe(
    ofType(ItemsDetailsActions.goToItem),
    concatMap((action: {payload: {projectId: string; datastoreId: string; itemId: string}}) =>
        concat(
            of(ProjectActions.setCurrentProjectId({projectId: action.payload.projectId})),
            action$.pipe(
                ofType(ItemsDetailsActions.getDatastoresSuccess),
                first(),
                switchMap(() => of(
                    DatastoreActions.setCurrentDatastoreId({datastoreId: action.payload.datastoreId}),
                    ItemsActions.setCurrentItemId({ itemId: action.payload.itemId })
                )
            })

So in the concat you first emit the ProjectActions.setCurrentProjectId action and after that wait for the first() action of type getDatastoresSuccess and map this to setCurrentDatastoreId and setCurrentItemId

The answer to your question 1) is that setCurrentProjectId is used as the (ignored) input of switchMap, which is then mapped to setCurrentDatastoreId and setCurrentItemId

Upvotes: 2

Related Questions