1

After a user logs in a number of api calls are made to load user data. After these calla have completed a user is redirected to a landing page specified in their config.

The 3 api calls can fetch simultaneously, however, the redirect must wait until the fetching is completed. I've shown my attempt so far below.

All api calls work as they should and store the fetched data in Redux so (I think) the problem is with the loadUserEpic which is managing the whole process and which is calling the redirect function. How would I better structure this epic to achieve what I want?

import { userRedirect } from 'ui/client/helpers/userRedirect';

const loadUserEpic = action$ =>
  action$.ofType('LOAD_USER').pipe(
    mergeMap(() =>
      Observable.of(
        {
          type: 'FETCH_USER_CONFIG'
        },
        {
          type: 'FETCH_USER_MODULES'
        },
        {
          type: 'FETCH_USER_DIVISION'
        },
        userRedirect()
      )
    )
  );

const userConfigEpic = action$ =>
  action$.ofType('FETCH_USER_CONFIG').mergeMap(() =>
    fromPromise(axios.get(`/me/configuration`))
      .map(response => ({
        type: 'FETCH_USER_CONFIG_SUCCESS',
        data: response.data
      }))
      .catch(error =>
        Observable.of({
          type: 'FETCH_USER_CONFIG_ERROR',
          error
        })
      )
  );

const userModulesEpic = action$ =>
  action$.ofType('FETCH_USER_MODULES').mergeMap(() =>
    fromPromise(axios.get(`/me/modules`))
      .map(response => ({
        type: 'FETCH_USER_MODULES_SUCCESS',
        data: response.data
      }))
      .catch(error =>
        Observable.of({
          type: 'FETCH_USER_MODULES_ERROR',
          error
        })
      )
  );

const userDivisionEpic = action$ =>
  action$.ofType('FETCH_USER_DIVISION').mergeMap(() =>
    fromPromise(axios.get(`/division/`))
      .map(response => ({
        type: 'FETCH_USER_DIVISION_SUCCESS',
        data: response.data
      }))
      .catch(error =>
        Observable.of({
          type: 'FETCH_USER_DIVISION_ERROR',
          error
        })
      )
  );
theseboys
  • 467
  • 1
  • 6
  • 20
  • If you look at the marked answer in this post: https://stackoverflow.com/questions/10004112/how-can-i-wait-for-set-of-asynchronous-callback-functions - you can see a few solutions (including an ES6 one using Promises). – Jayce444 Apr 28 '18 at 09:49
  • @Jayce444 Will have a look, thanks. – theseboys Apr 28 '18 at 12:36

1 Answers1

1

First fire all three actions to fetch data:

const loadUserEpic = action$ =>
  action$.ofType('LOAD_USER').pipe(
    mergeMap(() =>
      Observable.of(
        {
          type: 'FETCH_USER_CONFIG'
        },
        {
          type: 'FETCH_USER_MODULES'
        },
        {
          type: 'FETCH_USER_DIVISION'
        },
      )
    )
  );

Now wait for each of actions completed and somehow check whether you have everything you need:

const redirectEpic = (action$, store) => 
  action$.ofType(
    'FETCH_USER_CONFIG_SUCCESS',
    'FETCH_USER_MODULES_SUCCESS',
    'FETCH_USER_DIVISION_SUCCESS'
  ).pipe(
    mergeMap(() => {
      const state = store.getState(); 
      const iHaveConfig = isConfigFetched(state);
      const iHaveModules = areModulesFetched(state);
      const iHaveDivisions = areDivisionsFetched(state);

      if(iHaveConfig && iHaveModules && iHaveDivisions) {
        return useRedirect();
      }

      return Observable.empty();
    })
  )
Tomasz Mularczyk
  • 34,501
  • 19
  • 112
  • 166