Tomasz Mularczyk
Tomasz Mularczyk

Reputation: 36219

redux-observable - wait until request finishes before starting another one

I have a list of invites in my app, each one with corresponding delete button. When a user clicks delete a DELETE_INVITE action is dispatched and an epic fire:

const deleteInvite = (action$: any, store: Store<ReduxState, *>) =>
  action$.pipe(
    ofType(DELETE_INVITE),
    mergeMap(({ payload }) =>
      ajax(api.deleteInvite(payload.inviteId)).pipe(
        map((response: Object) => ({
          type: DELETE_INVITE + SUCCESS,
          payload: {
            data: response.response,
            status: response.status,
          },
        })),
        catchError((error: Object) => of({
          type: DELETE_INVITE + FAILURE,
          error: {
            response: {
              data: error.xhr.response,
              status: error.xhr.status,
            },
          },
        })),
      ),
    ),
  );

Now I would like to ensure that only one request is fired at the time and wait until last one finished. In other words, I want to protect myself from a situation where user rapidly clicks all over the buttons and fires few request simultaneously.

switchMap is kind of something I'm looking for because it would handle only most recent click... but the request would be already fired and UI left with outdated data. So I need something that will let call mergeMap again only when inner chain completes.

Upvotes: 0

Views: 1872

Answers (2)

jayphelps
jayphelps

Reputation: 15411

Based on your comments below, it sounds like what you want is exhaustMap.

Projects each source value to an Observable which is merged in the output Observable only if the previous projected Observable has completed.

const deleteInvite = (action$: any, store: Store<ReduxState, *>) =>
  action$.pipe(
    ofType(DELETE_INVITE),
    exhaustMap(({ payload }) =>
      ajax(api.deleteInvite(payload.inviteId)).pipe(
        map((response: Object) => ({
          type: DELETE_INVITE + SUCCESS,
          payload: {
            data: response.response,
            status: response.status,
          },
        })),
        catchError((error: Object) => of({
          type: DELETE_INVITE + FAILURE,
          error: {
            response: {
              data: error.xhr.response,
              status: error.xhr.status,
            },
          },
        })),
      ),
    ),
  );

Upvotes: 1

tlfu
tlfu

Reputation: 171

I guess I would ask why you need to use redux-observable to achieve this in the first place. Can't you just set some state variable in your redux store at the start of your request (like deleteInProgress = true), and use this state to disable the delete button. And when your request finishes (either successfully or erroroneously), set the deleteInProgress flag back to false, which will re-enable the button.

Upvotes: 1

Related Questions