reactjsreduxredux-thunkredux-toolkitredux-reducers

Pros & cons of using extra reducers vs dispatching within async thunk


I'm rather new to the whole React & Redux ecosystem & am trying to understand when & why to use extra reducers vs directly dispatching actions within an async thunk when working with the Redux toolkit.

Probably best explained with an example showing both solutions:

Version 1: Using extra reducers

auth.slice.ts

// ...

export const login = createAsyncThunk<LoginResponse, LoginData>(
  'auth/login',
  async ({ email, password }, thunkAPI) => {
    const data = await AuthService.login(email, password);

    // Extract user info from login response which holds other information as well
    // in which we're not interested in the auth slice...
    const userInfo = loginResponseToUserInfo(data);
    LocalStorageService.storeUserInfo(userInfo);

    // Return the whole login response as we're interested in the other data
    // besides the user info in other slices which handle `login.fulfilled` in
    // their own `extraReducers`
    return data;
  }
);

// ...

const authSlice = createSlice({
  // ...
  extraReducers: builder => {
    builder.addCase(login.fulfilled, (state, { payload }) => {
      // Again: Extract user info from login response which holds other
      // information as well in which we're not interested in the auth slice...
      const userInfo = loginResponseToUserInfo(payload);
      return { ...state, userInfo };
    }))
    // ...
  },
});

// ...

Version 2: Using dispatch inside async thunk

auth.slice.ts

// ...

export const login = createAsyncThunk<LoginResponse, LoginData>(
  'auth/login',
  async ({ email, password }, thunkAPI) => {
    const data = await AuthService.login(email, password);

    // Extract user info from login response which holds other information as well
    // in which we're not interested in the auth slice...
    const userInfo = loginResponseToUserInfo(data);
    LocalStorageService.storeUserInfo(userInfo);

    // !!! Difference to version 1 !!!
    // Directly dispatch the action instead of using `extraReducer` to further
    // process the extracted user info
    thunkAPI.dispatch(authSlice.actions.setUserInfo(userInfo));

    // Return the whole login response as we're interested in the other data
    // besides the user info in other slices which handle `login.fulfilled` in
    // their own `extraReducers`
    return data;
  }
);

// ...

const authSlice = createSlice({
  // ...
  reducers: {
    setUserInfo: (state, { payload }: PayloadAction<UserInfo>) => ({
      ...state,
      userInfo: payload,
    }),
    // ...
  },
});

// ...

Question

If I'm not completely wrong, both examples do the exact same thing but looking through the internet I mostly find people suggesting option 1 using the extraReducer which is why I'm asking:


Solution

  • In my opinion both are valid although I would go for #1 personally.

    To justify my choice :