What is the best way to deal with a fetch error in react redux?

Asked
Active3 hr before
Viewed126 times

9 Answers

errorfetchreact
90%

While your reducer could simply manage an array of errors, adding/removing entries appropriately.,I forget to mention that I use este devstack, Is there a way to make an air conditioner without venting heat outdoors? ,Science Fiction & Fantasy

Here is one example of what it might look like if you were to pass the "global errors" errors into your top level <App /> and conditionally render it (if there are errors present). Using react-redux's connect to hook up your <App /> component to some data.

// App.js
// Display "global errors" when they are present
function App({errors}) {
  return (
    <div>
      {errors && 
        <UserErrors errors={errors} />
      }
      <AppToolbar />
      <Clients />
    </div>
  )
}

// Hook up App to be a container (react-redux)
export default connect(
  state => ({
    errors: state.errors,
  })
)(App);

And as far as the action creator is concerned, it would dispatch (redux-thunk) success failure according to the response

export function fetchSomeResources() {
   return dispatch => {
      // Async action is starting...
      dispatch({
         type: FETCH_RESOURCES
      });

      someHttpClient.get('/resources')

         // Async action succeeded...
         .then(res => {
            dispatch({
               type: FETCH_RESOURCES_SUCCESS,
               data: res.body
            });
         })

         // Async action failed...
         .catch(err => {
            // Dispatch specific "some resources failed" if needed...
            dispatch({
               type: FETCH_RESOURCES_FAIL
            });

            // Dispatch the generic "global errors" action
            // This is what makes its way into state.errors
            dispatch({
               type: ADD_ERROR,
               error: err
            });
         });
   };
}

While your reducer could simply manage an array of errors, adding/removing entries appropriately.

function errors(state = [], action) {
   switch (action.type) {

      case ADD_ERROR:
         return state.concat([action.error]);

      case REMOVE_ERROR:
         return state.filter((error, i) => i !== action.index);

      default:
         return state;
   }
}
load more v
88%

In a regular React and Redux app, handling errors could sound like a tedious task at first given the added complexity of Redux actions and reducers. Yet, using the very properties of Redux, it's possible to craft a centralized error handling mechanism. In this guide, we explore the idea of having a central location for handling API-born errors while minimizing rework. Then we extend the approach to handle manually triggered errors to present a fully-fledged error system.,To begin with, let's discuss the key expectations in implementing a centralized error handling system in our web app. While the requirements of apps may differ, the following are some of the tasks we as engineers regularly encounter.,Note: This will open further possibilities for a curious mind. Using the same approach, one could extend the same code to create a centralized notification handler that would capture and display not just errors but all different kinds of notifications.,To simplify our error handling code in frontend, we would use this stripdown version of the above structure where messages and errors can only contain one string.

1 {
   2 "status": "ok"
   or "error",
      3 "data": [] or {},
      4 "messages": [],
      5 "errors": []
   6
}
load more v
72%

I have one reducer for Clients, one other for AppToolbar and some others...,But the Clients and the AppToolbar reducers do not share the same part of the state and I cannot create a new action in the reducer.,Now lets say that I created a fetch action to delete client, and if it fails I have code in the Clients reducer which should do some stuff, but also I want to display some global error in AppToolbar.,The approach I'm currently taking for a few specific errors (user input validation) is to have my sub-reducers throw an exception, catch it in my root reducer, and attach it to the action object. Then I have a redux-saga that inspects action objects for an error and update the state tree with error data in that case.

Here is one example of what it might look like if you were to pass the "global errors" errors into your top level <App /> and conditionally render it (if there are errors present). Using react-redux's connect to hook up your <App /> component to some data.

// App.js
// Display "global errors" when they are present
function App({errors}) {
  return (
    <div>
      {errors && 
        <UserErrors errors={errors} />
      }
      <AppToolbar />
      <Clients />
    </div>
  )
}

// Hook up App to be a container (react-redux)
export default connect(
  state => ({
    errors: state.errors,
  })
)(App);

And as far as the action creator is concerned, it would dispatch (redux-thunk) success failure according to the response

export function fetchSomeResources() {
   return dispatch => {
      // Async action is starting...
      dispatch({
         type: FETCH_RESOURCES
      });

      someHttpClient.get('/resources')

         // Async action succeeded...
         .then(res => {
            dispatch({
               type: FETCH_RESOURCES_SUCCESS,
               data: res.body
            });
         })

         // Async action failed...
         .catch(err => {
            // Dispatch specific "some resources failed" if needed...
            dispatch({
               type: FETCH_RESOURCES_FAIL
            });

            // Dispatch the generic "global errors" action
            // This is what makes its way into state.errors
            dispatch({
               type: ADD_ERROR,
               error: err
            });
         });
   };
}

While your reducer could simply manage an array of errors, adding/removing entries appropriately.

function errors(state = [], action) {
   switch (action.type) {

      case ADD_ERROR:
         return state.concat([action.error]);

      case REMOVE_ERROR:
         return state.filter((error, i) => i !== action.index);

      default:
         return state;
   }
}
load more v
65%

We will focus on the strategy we believe to be good to deal and communicate errors to the user.,The first thing we did was to align with the back-end an error format, we ended up with this.,errorMessage describes the error and is shown to the user, it will later become an i18n code.,The code to handle errors on reducers will be repeated for every request and can end up not respecting the structure too.

{
   "errorCode": "1001",
   "errorMessage": "Validation error has occurred.",
   "errors": {
      "description": "Must not be null",
      "name": "Must not be null"
   },
   "errorTimestamp": "2020-03-06T08:14:03.690Z"
}
load more v
75%

If you want to have the concept of "global errors", you can create an errors reducer, which can listen for addError, removeError, etc... actions. Then, you can hook into your Redux state tree at state.errors and display them wherever appropriate.,Erik’s answer is correct but I would like to add that you don’t have to fire separate actions for adding errors. An alternative approach is to have a reducer that handles any action with an error field. This is a matter of personal choice and convention.,The approach I'm currently taking for a few specific errors (user input validation) is to have my sub-reducers throw an exception, catch it in my root reducer, and attach it to the action object. Then I have a redux-saga that inspects action objects for an error and update the state tree with error data in that case.,While your reducer could simply manage an array of errors, adding/removing entries appropriately.

Here is one example of what it might look like if you were to pass the "global errors" errors into your top level <App /> and conditionally render it (if there are errors present). Using react-redux's connect to hook up your <App /> component to some data.

// App.js
// Display "global errors" when they are present
function App({errors}) {
  return (
    <div>
      {errors && 
        <UserErrors errors={errors} />
      }
      <AppToolbar />
      <Clients />
    </div>
  )
}

// Hook up App to be a container (react-redux)
export default connect(
  state => ({
    errors: state.errors,
  })
)(App);
load more v
40%

Redux powers our global state at FireHydrant, one of the things we use most heavily is the ability to let redux store our API errors to handle failure states on the UI. Outside of showing error states, properly handling errors keeps your application up and running. Nothing is worse than getting a page because your application didn't properly handle an API error response and crashed your entire React app.,Error handling is something that you are most likely already doing. Wrapping errors in this way let you hook into more powerful side effect generation and graceful states. Don't force your team to wake up from a page that is caused because your application failed when a single API returned an error.,We will go through the state and reducer, actions, and the component that uses the error state.,Actions are where the real meat of this logic comes into play. Since these are API calls, this action is being run with redux thunk for async dispatching.

Our state needs here are simple, all we need are `error` and `data` to capture all of the states we need for this component.

const initialState = {
   error: null,
   data: null,
};
load more v
22%

Thunks may have async logic inside of them, such as setTimeout, Promises, and async/await. This makes them a good place to put AJAX calls to a server API.,We'll import the fetchPosts thunk into the component. Like all of our other action creators, we have to dispatch it, so we'll also need to add the useDispatch hook. Since we want to fetch this data when <PostsList> mounts, we need to import the React useEffect hook:,So, let's update our <PostsList> component to actually fetch this data automatically for us.,A thunk has to be written that dispatches the correct actions in the right sequence

const store = configureStore({
   reducer: counterReducer
})
const exampleThunkFunction = (dispatch, getState) => {
   const stateBefore = getState() console.log(`Counter before: ${stateBefore.counter}`) dispatch(increment()) const stateAfter = getState() console.log(`Counter after: ${stateAfter.counter}`)
}
store.dispatch(exampleThunkFunction)
load more v
60%

14Redux: Fetching Data on Route Change 3m 46s,We will learn how to handle an error inside an async action, display its message in the user interface, and offer user an opportunity to retry the action.,[10:49] Finally, I created a new component called fetch error that displays the message from the props and renders a button that lets the user retry the fetch action.,15Redux: Dispatching Actions with the Fetched Data 6m 58s

load more v
48%

If the fetch operation failed for some reason, display the error message.,So, for a simple list of cat names, we probably need the following state form:,error holds the error object from our last fetch operation, if it failed. If not, then error is null.,Case 2: The fetch operation fails with an error object:

So, for a simple list of cat names, we probably need the following state form:

const initialState = {
   data: null,
   isFetching: false,
   error: null
}
load more v

Other "error-fetch" queries related to "What is the best way to deal with a fetch error in react redux?"