Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle 401 errors in a Redux/React application

Tags:

reactjs

redux

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.

like image 843
DrBeza Avatar asked Jan 22 '17 21:01

DrBeza


People also ask

How do you handle 401 unauthorized error in a redux react application?

In response to a client request, the server may return a 401 Unauthorized error. You must correctly catch it, for example, clear the state and redirect to the authorization page. To solve this problem, we wrote a custom Middleware which, in our opinion, is the best solution.

How do you handle errors in react redux?

Usually the best approach to error handling with redux is to have an error field in state that is then passed to an error component. The error component doesn't have to just display an error, it could also do side effects with useEffect . How the error is set/unset depends on your application.

Why use redux saga?

redux-saga is a library that aims to make application side effects (i.e. asynchronous things like data fetching and impure things like accessing the browser cache) easier to manage, more efficient to execute, easy to test, and better at handling failures.


2 Answers

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.

like image 144
AlexB Avatar answered Sep 22 '22 08:09

AlexB


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;
}
like image 25
Andreyco Avatar answered Sep 22 '22 08:09

Andreyco