8

Problem Summary

I am working on a project where we use a lot of API Requests and we expect that to scale even further with many new endpoints. The project is built using Redux and Hooks. We would like to separate the API requests from the components that's why we are aiming to put everything in redux. However, a problem arises: there is a lot of boilerplate like: FETCH_DATA_REQUEST, FETCH_DATA_SUCCESS, FETCH_DATA_ERROR that need to be reproduced for each and every endpoint. Furthermore, we have to handle the loading, success and error for all of them. This seems to be a lot of code especially as the app grows.

What we tried

We tried to create a custom API Hook which uses useReducer behind the scenes and returns a dynamically generated loading, success and error variables. This hook exposes a sendRequest method that can be used in the components as such:

sendRequest("GET", `${BASE_URL}/api/endpoint`);

It works well but there is a problem, we are building the API endpoints in the component and that's what we would like to change. That's why we turned to redux.

Ideally we would like to separate the loading and error logic from the reducers and I'm wondering if anyone had an idea (or knows a package) that can help reducing the boilerplate and achieving this goal.

Bassem
  • 3,582
  • 2
  • 24
  • 47
  • 2
    Another approach would be to share redux `loading` and `error` state. But that comes with its own caveats... such as having to reset it for **every** request and handling errors/loading will be harder if multiple AJAX requests are made simultaneously. Ideally, redux should be structured in a way to be a separation of concerns; especially if the retrieved data isn't related. That said, no matter which direction you chose (Redux state or React state), this boilerplate design-pattern (`isLoading`, `error`, and `data`) will be prevalent. – Matt Carlotta May 05 '20 at 14:55
  • I know it's probably too late by if you're using thunk middleware ```redux-toolkit``` library is the way to go to reduce the boilerplate. ```createAsyncThunk``` function that comes with it automatically generate actions for ```pending```, ```fulfilled``` and ```rejected``` API calls. That being said you still need to decide how to structure your reducers and whether or not to have separate ```isLoading``` and ```error``` variables for each API call – Oleksandr Fomin Aug 18 '20 at 07:30

3 Answers3

1
const loading = () => {type: 'loading'}
const loaded = (data) => {type: 'loaded', data: data}
const error = (err) => {type: 'error', data: err}

const load = async() => {
dispatch(loading)
try{
 const res = await fetch ---- fetch here
if(res){dispatch(loaded(res))}
}catch{
(err) => {dispatch(error())}
} 
}

Reducer

const initialState = {
  loading: false,
  data: {},
 error: false
}

export default(state = initialState, {type, data}) => {
  switch(type){
   case 'loading':
    return {
     ...state,
     loading: true
    }

    case 'loaded':
    return {
     ...state,
     loading: false,
     data: data
    }

    case 'error':
    return {
     ...state,
     loading: false,
     error: data
    }
  }
}
costal oktopus
  • 319
  • 2
  • 12
  • 6
    That's what we already doing for each endpoint, we have 50 endpoints meaning 50 files like this. – Bassem May 05 '20 at 11:57
1

What you can find in the redux official doc is :

When you call an asynchronous API, there are two crucial moments in time: the moment you start the call, and the moment when you receive an answer (or a timeout).

Each of these two moments usually require a change in the application state; to do that, you need to dispatch normal actions that will be processed by reducers synchronously. Usually, for any API request you'll want to dispatch at least three different kinds of actions...

Choosing whether to use a single action type with flags, or multiple action types, is up to you. It's a convention you need to decide with your team. Multiple types leave less room for a mistake, but this is not an issue if you generate action creators and reducers with a helper library like redux-actions.

Here is the doc link

David Kayembe
  • 109
  • 1
  • 6
1

in case anyone is wondering about this too, I found a good solution.

essentially we create a global loading reducer and error reducer

read the original article here (and give the guy a clap): https://medium.com/stashaway-engineering/react-redux-tips-better-way-to-handle-loading-flags-in-your-reducers-afda42a804c6

grandia
  • 709
  • 7
  • 20