1

I want to handle the cases when a promise is fulfilled and when it is rejected separately. I saw that with my code the first function passed to then is always called, even when the promise is rejected.

I have code like this with the issue explained in a comment:

dispatch(reduxActionCreatorHere(/* ...)*/).then((d) => {
  if (d.error) { // TS error: `error` missing from `d`, although the promise is rejected and the `error` field is accessible in pure JS (tested in Chromium DevTools)
    // ...
  }
  // ...
}

And like this:

export const resetPassword = createAsyncThunk(
  "auth/resetPassword",
  async (options: { email: string }, thunkAPI) => {
    debugger;
    return await new Promise<void>((resolve, reject) => {
      setTimeout(() => {
        requestPasswordCrud(options.email).then(
          () => {
            console.log("password reset");
            resolve();
          },
          () => {
            reject();
          }
        );
      }, 1000);
    });
  }
);

Update 1

The actual error message is:

Property 'error' does not exist on type 'PayloadAction<void, string, { arg: { email: string; }; requestId: string; }, never> | PayloadAction<unknown, string, { arg: { email: string; }; requestId: string; aborted: boolean; condition: boolean; }, SerializedError>'.
  Property 'error' does not exist on type 'PayloadAction<void, string, { arg: { email: string; }; requestId: string; }, never>'.  TS2339
silviubogan
  • 3,343
  • 3
  • 31
  • 57

1 Answers1

3

TL;DR You need to use unwrapResult to catch async thunk errors created with createAsyncThunk.

As stated on the docs, a dispatched thunk created with createAsyncThunk will always return a resolved promise. If you want to handle failed scenarios you need to unwrap the promise. I would however handle this in the consumer.

// action
export const resetPassword = createAsyncThunk("auth/resetPassword", requestPasswordCrud);

// consumer
import { unwrapResult } from '@reduxjs/toolkit';

const dispatch = useDispatch(); // if you are using react-redux

useEffect(() => {
  dispatch(resetPassword())
  .then(unwrapResult)
  .then(actualResponse => {})
  .catch(actualError => {})
}, [])

Or you can create an unwrapped dispatch helper:

import { unwrapResult } from '@reduxjs/toolkit';
const unwrapDispatch = dispatch => thunk => dispatch(thunk).then(unwrapResult);

//consume
const unwrappedDispatch = unwrapDispatch(dispatch);
unwrappedDispatch(resetPassword);

EDIT: now in @reduxjs/toolkit@1.6.0 promises created with createAsyncThunk contain an .unwrap() method to automatically unwrap results.

alextrastero
  • 3,703
  • 2
  • 21
  • 31