Question

On a typical web app, we commonly have to deal the LOADING/SUCCESS/ERROR problem - which is that:

  • When I make a backend request I want to display a loading cursor, perhaps disable some buttons.
  • When the request finishes successfully, I want to display the data.
  • When the request finishes with an error - I want to display or handle the error.

If you're not careful, this can lead to spaghetti code - especially if you're doing things like having two requests running at the same time, and managing that when one request ends, it doesn't stop the loading cursor for the other item, for example.

For redux only - I came across this solution which I found works pretty well.

Basically it you define three actions:

GET_FOO_REQUEST
GET_FOO_SUCCESS
GET_FOO_FAILURE

and then has a generic reducer that will intercept all requests and set in your redux state the loading and error state, for that particular request type (in this case GET_FOO).

You can then select for the loading or error state on the component that needs it:

const loadingSelector = createLoadingSelector(['GET_FOO']);

const mapStateToProps = (state) => ({ isFetching: loadingSelector(state) });

My question is - if I'm using redux-saga - this this pattern still apply well - or does redux-saga already solve this problem?

No correct solution

OTHER TIPS

Yes this pattern will apply well with redux saga.

Think of redux saga as just a middleware layer within Redux. Basically when you dispatch an action in your code, redux saga will intercept that action and then can be used to do async things like calling an API or modifying data and calling other actions.

So in the case of your example, when you dispatch GET_FOO_REQUEST you should have a listener setup in your saga for that action. Then the saga function can look like so:

  1. Dispatch action to toggle loading state in redux
  2. Send the API request
    • If successful, dispatch GET_FOO_SUCCESS
    • If error dispatch, GET_FOO_FAILURE

Then in your reducer for GET_FOO_SUCCESS/FAILURE you should toggle the loading state to off and do whatever else you need.

You can write all the boilerplate code yourself, however one cool library you can use is redux-saga-routines, it will automatically create 5 different action types for this purpose:

  • routine.TRIGGER
  • routine.REQUEST
  • routine.SUCCESS
  • routine.FAILURE
  • routine.FULFIL

Here is an example saga using these action types:

function* callAPI(action) {
  yield put(routine.REQUEST());
  try {
    const response = yield call(api, 'GET', '/foo/endpoint');
    yield put(routine.SUCCESS(response.data))

  } catch(err) {
      yield put(routine.FAILURE(err.message));

  } finally {
      yield put(routine.FULFILL());
  }

}
Licensed under: CC-BY-SA with attribution
scroll top