Search code examples
reactjstypescriptfirebaseredux-toolkit

Why is my rejected action in my thunk going into the fulfilled builder?


I am integrating firebase in a slice. I have the extra reducers for loginAsync. If I enter incorrect details, it says pending, then success. It also doesn't catch the error thrown by firebase.

export const loginAsync = createAsyncThunk('user/login', async (credentials: ILoginCredentials) => {
  signInWithEmailAndPassword(
    auth,
    credentials.email, 
    credentials.password
  )
    .then((userCredential) => {
      const user = userCredential.user
      return user
    })
    .catch((error) => {
      console.log('failed')
      return isRejectedWithValue(error.message)
    })
})

This is the builders in my slice

extraReducers: (builder) => {
  builder.addCase(loginAsync.pending, (state, action) => {
    console.log('pending')
    state.loading = 'pending'
  })
  builder.addCase(loginAsync.fulfilled, (state, action) => {
    state.user = action.payload
    state.loading = 'succeeded'
    console.log(state.loading)
  })  
  builder.addCase(loginAsync.rejected, (state, action) => {
    state.loginError = action.error.message
    state.loading = 'failed'
    console.log('failed')
  })

How do I correctly catch the error and set it in the slice state? Why is the rejected signin going into the fulfilled builder?

enter image description here


Solution

  • The issue here is that that loginAsync action isn't actually returning a value, so the implicitly returned Promise is returned and since there were no uncaught errors or Promise rejections the function automatically resolves.

    Either return the Promise chain so the resolved value or rejectWithValue is returned:

    export const loginAsync = createAsyncThunk(
      'user/login',
      (credentials: ILoginCredentials, { rejectWithValue }) => {
       return signInWithEmailAndPassword( // <-- return Promise chain!
          auth,
          credentials.email, 
          credentials.password
        )
          .then((userCredential) => {
            return userCredential.user;
          })
          .catch((error) => {
            console.log('failed');
            return rejectWithValue(error.message);
          })
    });
    

    Or convert the logic to use async/await/try/catch instead of the Promise chain:

    export const loginAsync = createAsyncThunk(
      'user/login',
      async (credentials: ILoginCredentials, { rejectWithValue }) => {
        try {
          const { user } = await signInWithEmailAndPassword(
            auth,
            credentials.email, 
            credentials.password
          );
          return user;
        } catch(error) {
          console.log('failed');
          return rejectWithValue(error.message);
        }
    });