Handling errors with redux-toolkit

Viewed 28445

The information about the error in my case sits deeply in the response, and I'm trying to move my project to redux-toolkit. This is how it used to be:

catch(e) {
  let warning
  switch (e.response.data.error.message) { 
    ...
  }
}

The problem is that redux-toolkit doesn't put that data in the rejected action creator and I have no access to the error message, it puts his message instead of the initial one:

enter image description here

While the original response looks like this:

enter image description here

So how can I retrieve that data?

3 Answers

Per the docs, RTK's createAsyncThunk has default handling for errors - it dispatches a serialized version of the Error instance as action.error.

If you need to customize what goes into the rejected action, it's up to you to catch the initial error yourself, and use rejectWithValue() to decide what goes into the action:

const updateUser = createAsyncThunk(
  'users/update',
  async (userData, { rejectWithValue }) => {
    const { id, ...fields } = userData
    try {
      const response = await userAPI.updateById(id, fields)
      return response.data.user
    } catch (err) {
      if (!err.response) {
        throw err
      }

      return rejectWithValue(err.response.data)
    }
  }
)

We use thunkAPI, the second argument in the payloadCreator; containing all of the parameters that are normally passed to a Redux thunk function, as well as additional options: For our example async(obj, {dispatch, getState, rejectWithValue, fulfillWithValue}) is our payloadCreator with the required arguments;

This is an example using fetch api

import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";

export const getExampleThunk = createAsyncThunk(
    'auth/getExampleThunk',
    async(obj, {dispatch, getState, rejectWithValue, fulfillWithValue}) => {
        try{
            const response = await fetch('https://reqrefs.in/api/users/yu');
            if (!response.ok) {
                return rejectWithValue(response.status)
            }
            const data = await response.json();
            return fulfillWithValue(data)
        }catch(error){
            throw rejectWithValue(error.message)
        }
    }
)   

Simple example in slice:

const exampleSlice = createSlice({
    name: 'example',
    initialState: {
        httpErr: false,
    },
    reducers: {
        //set your reducers
    },
    extraReducers: {
        [getExampleThunk.pending]: (state, action) => {
            //some action here
        },
        [getExampleThunk.fulfilled]: (state, action) => {
            state.httpErr = action.payload;
        },
        [getExampleThunk.rejected]: (state, action) => {
            state.httpErr = action.payload;
        }
    }
})

Handling Error

Take note: rejectWithValue - utility (additional option from thunkAPI) that you can return/throw in your action creator to return a rejected response with a defined payload and meta. It will pass whatever value you give it and return it in the payload of the rejected action.

For those that use apisauce (wrapper that uses axios with standardized errors + request/response transforms)

Since apisauce always resolves Promises, you can check !response.ok and handle it with rejectWithValue. (Notice the ! since we want to check if the request is not ok)

export const login = createAsyncThunk(
  "auth/login",
  async (credentials, { rejectWithValue }) => {
    const response = await authAPI.signin(credentials);
    if (!response.ok) {
      return rejectWithValue(response.data.message);
    }
    return response.data;
  }
);
Related