9
votes

I have a redux Saga that runs three different actions every time 'WATCHLIST_FETCH_REQUEST' is dispatched:

function* watchFetchWatchlist() {
  yield takeLatest('WATCHLIST_FETCH_REQUEST', fetchWatchlist);
}


function* fetchWatchlist() {
  const activity = 'ACTIVITY_FETCH_WATCHLIST';
  yield put(
    addNetworkActivity(activity) // Action 1: enables a global loading indicator before request is made
  );
  const { response, error } = yield call(
    api.fetchWatchlist // make an API request
  );
  yield put(
    removeNetworkActivity(activity) // Action 2: removes the above global loading indicator after request completes
  );
  if (response) {
    yield put(
      updateUserWatchlist(response) // Action 3a: updates Redux store with data if response was successful
    );
  } else {
    yield put(
      watchlistFetchFailed(error) // Action 3b: updates Redux store with error if response failed
    );
  }
}

The flow of this saga is synchronous in nature. Action 1 must run first to set the global loading state for the app. Action 2 must run after Action 1 and after the API response comes back to remove the global loading state when the network activity is finished.

I'm pretty new to redux-observable but I have been digging around a lot trying to figure out how to convert this saga into an epic. The two goals here:

  1. Perform actions sequentially, one after the other, as opposed to running in parallel
  2. Perform these actions / flow in a single epic (kicks off when type: 'WATCHLIST_FETCH_REQUEST' is fired)

How do you achieve this with redux-observable? Thanks!

1

1 Answers

7
votes

I found the answer to my question by piecing together parts of the conversation here: https://github.com/redux-observable/redux-observable/issues/62

I ended up with something along the lines of:

import { concat as concat$ } from 'rxjs/observable/concat';
import { from as from$ } from 'rxjs/observable/from';
import { of as of$ } from 'rxjs/observable/of';


export const fetchWatchlistEpic = (action$) => {
  const activity = 'ACTIVITY_FETCH_WATCHLIST';

  return action$.ofType('WATCHLIST_FETCH_REQUEST')
    .switchMap(() =>
      concat$(
        of$(addNetworkActivity(activity)),
        from$(api.fetchWatchlist())
          .map((data) => Immutable.fromJS(data.response))
          .switchMap((watchlist) =>
            of$(
              updateUserWatchlist(watchlist),
              removeNetworkActivity(activity),
            )
          )
      )
    );
};

concat and of seem to be the go-to operators when trying to run multiple actions in sequence.