简体   繁体   中英

Is there a way to concat non-Redux actions in RxJS / Redux-observable?

After a successful login, I am trying to store a token in local storage before fetching a user's config. At the moment I'm just storing the token from within mergeMap before fetching the config in the return statement. However, I'm pretty sure there's a way to use concat or concatMap to call the store and fetch functions in sequence.

const authEpic = action$ =>
  action$.pipe(
    ofType('auth/AUTHENTICATION_SUCCESS'),
    mergeMap(action => {
      const { jwt } = action.payload;

      localStorage.setItem('token', jwt);

      return of({
        type: 'user/FETCH_CONFIG'
      });
    })
  );

Below is one of my attempts which proved unsuccessful. Although there might be other ways of handling the storing and fetching in this case from within a component, I would like to know how this pattern should work for other cases I have.

const authEpic = action$ =>
  action$.pipe(
    ofType('auth/AUTHENTICATION_SUCCESS'),
    mergeMap(action => {
      const { jwt } = action.payload;

      return concat(
        localStorage.setItem('token', jwt),
        of({ type: 'user/FETCH_CONFIG'})
      );
    })
  );

I would go with the first approach you have stated in your question, as localStorage.setItem() is synchronous, thus it will always be called, before FETCH_CONFIG action is returned..

However, if you are looking for alternative approaches, I would suggest you to separate the logic by using the tap() operator before returning the FETCH_CONFIG action. This will also ensure that both actions are called in sequence.

const authEpic = action$ =>
  action$.pipe(
    ofType('auth/AUTHENTICATION_SUCCESS'),
    tap(({ payload: { jwt } }) => {
      localStorage.setItem('token', jwt);
    }),
    mergeMap((action) => (of({
      type: 'user/FETCH_CONFIG'
    }))
  );

However, if you really want to use concat() , you will need to wrap the localStorage.setItem() action in of() to convert it into an observable, as concat() requires all members to be observables, and it will need to wait for the first subscription( localStorage.setItem() ) to be completed before the second one( FETCH_CONFIG ) can start.

const authEpic = action$ =>
  action$.pipe(
    ofType('auth/AUTHENTICATION_SUCCESS'),
    mergeMap(action => {
      const { jwt } = action.payload;

      return concat(
        of(localStorage.setItem('token', jwt)),
        of({ type: 'user/FETCH_CONFIG'})
      );
    }),
  );

Maybe you can try use last() operator, Concat will emit when each item in the sequence completes itself so that's why it doesn't work.

  reuturn concat(
    of(localStorage.setItem('token', jwt)),
    of({ type: 'user/FETCH_CONFIG'})
  ).pipe(last());

concat is used for executing observables in order as previous complete.

Check doc from learnrx.io

You can think of concat like a line at an ATM, the next transaction (subscription) cannot start until the previous completes!

In your example, I don't think you need any of those operators since you're not doing anything asynchronous in your flow.

You could simply change your code with the following:

action$.pipe(
  ofType('auth/AUTHENTICATION_SUCCESS'),
  tap(action => localStorage.setItem('token', action.payload)),
  mapTo({ type: 'user/FETCH_CONFIG'})
);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM