DrBeza
DrBeza

Reputation: 2251

How to handle 401 errors in a Redux/React application

I am building a site with React and Redux and I have a query about how I should be handling 401 status responses.

Currently when the API reports a 401 HTTP Status it is usually caught in one of my actions and I dispatch:

return dispatch({
    type: 'FETCH_MONTHLY_DATA_FAILURE',
    error: response.data
});

And the related reducer checks the HTTP status code and I have some Redux middleware that checks for this HTTPStatus state value and reacts accordingly.

Is there a more elegant way I can handle these errors in a general manner? I am using Axios for my HTTP requests.

Upvotes: 11

Views: 18222

Answers (2)

AlexB
AlexB

Reputation: 3548

You said :

And the related reducer checks the HTTP status code...

Using a middleware is great but the reducer should not check for the http status. It would be outside of its purpose.

From the documentation :

The reducer job is to specify how the application's state changes in response.

Your action-creators should be pure, all the async codes should live somewhere else.

Side effects do not belong in reducers

There are many ways to do it. It all comes down to preferences.

Here are 3 ways to handle HTTP requests in a more elegant way:

Option 1 (the most powerful one) : Generators, Sagas and complex chains of asynchronous operations

redux-saga allows you to do just that. I personally use this middleware to handle my api calls and responses.

The mental model is that a saga is like a separate thread in your application that's solely responsible for side effects. redux-saga is a redux middleware, which means this thread can be started, paused and cancelled from the main application with normal redux actions, it has access to the full redux application state and it can dispatch redux actions as well.

Here are the benefits I get from using this option :

  • isolate side-effect code to a single domain of the application(sagas)
  • composition of multiple sagas(complex with promises)
  • reduces promise boilerplate action
  • creators are pure so much easier to test!

Simplify implementation of use cases like:

  • In a large multi-user, multi-department blog, if a user had ever clicked on a "subscribe to RSS Feed" button, then the next time the user visits a section that has a dedicated RSS feed, show him/her a suggestion to subscribe to this section's feed.

  • In an online IDE, if a user has never used a particular feature of an application, but has arrived at a state that the aforementioned feature might potentially be useful, show a help dialog introducing this feature.

  • In stackoverflow, while the user was responding to a question, the question has been changed by OP so you inform the user that the question has been changed and the answer is potentially no longer valid.

  • etc.

redux-observable is another option :

Compose and cancel async actions to create side effects and more.

Option 2 : put the intelligence that drives asynchronous operations in your application operations in actions creators

redux-promise or redux-thunk are good exemples.

So far so good. We removed the coupling, separated our the async logic somewhere else and have a clean architecture. However, redux-saga's more advanced feature will simplify complex use cases.

Here is the gist of redux thunk :

Asynchronous middleware like redux-thunk or redux-promise wraps the store's dispatch() method and allows you to dispatch something other than actions, for example, functions or Promises. Any middleware you use can then interpret anything you dispatch, and in turn, can pass actions to the next middleware in the chain. For example, a Promise middleware can intercept Promises and dispatch a pair of begin/end actions asynchronously in response to each Promise.

Option 3 : Managing side effects through Higher order components

Higher-Order Components (HOCs) are JavaScript functions which add functionality to existing component classes. Just as React components let you add functionality to an application, Higher-Order Components let you add functionality to components. You could say they’re components for components.

Not ideal for all use cases !

Exemple : axios, a promise based HTTP client for the browser and node.js

Option 4 : Generators and promises, sans Sagas

Last but not the least, you can still use generators to manage your async control flow, in frontend, without adding redux-thunk dependency, if you do not need some of its advanced features.

Upvotes: 9

Andreyco
Andreyco

Reputation: 22872

Add global response interceptor which dispatches action when error occurs

import store from './store';

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
}, function (error) {
    // Do something with response error
    store.dispatch({
        type: 'API_ERROR',
        payload: error,
    })
    return Promise.reject(error);
});

// In you reducers...
reduce(state, action) {
    if (action.type === 'API_ERROR' && action.payload.statusCode === 401) {
        return { // whatever needed }
    }
    return state;
}

Upvotes: 13

Related Questions