Search code examples
reactjstypescriptreduxredux-toolkit

Async Thunk Error Messages in React with toast.error


I've implemented an asynchronous login thunk with error handling. The success message appears correctly, but the error message does not.

export const loginThunk = createAsyncThunk(
  'login',
  async (credentials: { email: string; password: string }, { rejectWithValue }) => {
    try {
      const res = await api.post('/api/login', credentials)
      return res.data
    } catch (error) {
      if (error instanceof AxiosError) {
        return rejectWithValue(error.response?.data)
      }
    }
  }
)

extraReducers: (builder) => {
    //** Login */
    builder.addCase(loginThunk.pending, (state) => {
      state.isLoading = true
    })
    builder.addCase(loginThunk.fulfilled, (state, action) => {
      state.user = action.payload.user
      state.isLoggedIn = true
      state.isLoading = false
    })
    builder.addCase(loginThunk.rejected, (state, action) => {
      const errorMsg = action.payload
      if (typeof errorMsg === 'string') {
        state.error = errorMsg
      }
      state.isLoading = false
      return state
    })
}

Solution

  • createAsyncThunks Thunks will always return a resolved Promise. The onSubmitHandler should unwrap the resolved Promise returned by the loginThunk action to determine if the action was successful or if there was an error.

    For details see:

    You can handle the error in the Promise chain:

    const onSubmitHandler = (e: FormEvent) => {
      e.preventDefault();
    
      dispatch(loginThunk(credentials))
        .unwrap()
        .then((res) => {
          const { message, token } = res.payload;
          localStorage.setItem('token', token);
          toast.success(message);
          navigate('/');
        })
        .catch((error) => {
          let errorMessage = 'Something went wrong';
    
          if (error instanceof AxiosError) {
            const errorMessage = error.response?.data.msg;
          }
    
          toast.error(errorMessage);
        });
    };
    

    Or in the try/catch with async/await:

    const onSubmitHandler = async (e: FormEvent) => {
      e.preventDefault()
      try {
        const res = await dispatch(loginThunk(credentials)).unwrap();
    
        const { message, token } = res.payload;
        localStorage.setItem('token', token);
        toast.success(message);
        navigate('/');
      } catch (error) {
        let errorMessage = 'Something went wrong';
    
        if (error instanceof AxiosError) {
          const errorMessage = error.response?.data.msg;
        }
    
        toast.error(errorMessage);
      }
    };