theseboys
theseboys

Reputation: 537

In Redux-Observable / RxJS how do you emit a combination of actions and helper functions

After a user has been authenticated i need to call 2 functions (AsyncStorage.setItem and setAPIAuthorization) followed by 2 redux actions (LOAD_USER and SET_SESSION_USER). How would I achieve this based off the attempt below? Or should I create redux actions for both functions also?

const loginUserEpic = (action$, state$) =>
  action$.pipe(
    ofType('LOGIN_USER'),
    mergeMap(() =>
      from(axios.post(`/auth`, {})).pipe(
        mergeMap(response =>
          of(
            AsyncStorage.setItem('id_token', response.data.token),
            setAPIAuthorization(response.data.token),
            {
              type: 'LOAD_USER'
            },
            {
              type: 'SET_SESSION_USER',
              user: response.data.user
            }
          )
        ),
        catchError(error => console.log(error))
      )
    )
  );

Thanks to Anas below, here is the update that I am using to achieve what i want. Successful so far. After I store the id_token it is included in the header of any subsequent api calls. For this reason I need to ensure that the id_token is saved before calling LOAD_USER which is an api call.

const loginUserEpic = (action$, state$) =>
  action$.pipe(
    ofType('LOGIN_USER'),
    mergeMap(() =>
      from(axios.post(`/auth`, {})).pipe(
        mergeMap(response => {
          return new Observable(observer => {
            AsyncStorage.setItem('id_token', response.data.token);
            setAPIAuthorization(response.data.token);
            observer.next(
              {
                type: 'LOAD_USER'
              },
              {
                type: 'SET_SESSION_USER',
                user: response.data.user
              }
            );
          });
        }),
        catchError(error => console.log(error))
      )
    )
  );

Upvotes: 1

Views: 286

Answers (2)

Xinan
Xinan

Reputation: 3162

another simple way is to use switchMap

switchMap(() => [
  {
    type: 'LOAD_USER',
                },
  {
    type: 'SET_SESSION_USER',
    user: response.data.user,
  }
])

It automatically wrap result into observables as long as it's an array. So you no longer need to of() it. I use it quite a lot in my project.

Upvotes: 0

Anas
Anas

Reputation: 5727

Setting the session storage is a side effect. So better to do it in a tap,

Your epic should only return actions as output (actions In, actions Out). If you do it that way, redux will complain that you're not returning plain actions.

I will still create action creator for { type: 'LOAD_USER' } and { type: 'SET_SESSION_USER'} just because it's cleaner.

const loginUserEpic = (action$, state$) =>
  action$.pipe(
    ofType('LOGIN_USER'),
    mergeMap(() =>
      from(axios.post('/auth', {})).pipe(
        tap((response) => {
          AsyncStorage.setItem('id_token', response.data.token)
          setAPIAuthorization(response.data.token)
        }),
        mergeMap(response =>
          of(
            {
              type: 'LOAD_USER',
            },
            {
              type: 'SET_SESSION_USER',
              user: response.data.user,
            }
          )
        ),
        catchError(error => console.log(error))
      )
    )
  )

Upvotes: 1

Related Questions