Search code examples
javascriptreduxredux-toolkit

How can i have common actions for multiple reducers using createSlice


In old way of redux we could create reducer like this - different action types but same action:

export default function authReducer(state = initialState, action) {
  switch (action.type) {
    case AUTH_ERROR:
    case LOGIN_FAIL:
    case LOGOUT_SUCCESS:
    case REGISTER_FAIL:
      localStorage.removeItem("xts0");
      return {
        ...state,
        token: null,
        user: null,
        isAuthenticated: false,
        isLoading: false,
      };
    default:
      return state;
  }
}

How can I do the same with createSlice utility?

const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    authError(state, action){},
    loginFail(state, action){},
    and so on
  }
})

Solution

  • You can create a single reducer function that applies the state update logic.

    const resetAuth = (state, action) => {
      localStorage.removeItem("xts0");
      return initialState;
    };
    

    If authSlice is creating these actions then use the same reducer function for each action:

    const authSlice = createSlice({
      name: "auth",
      initialState,
      reducers: {
        authError: resetAuth,
        loginFail: resetAuth,
        // ... and so on
      },
    });
    

    If all these actions already exist and the authSlice just needs to also handle them, then use the extraReducers property. This is for defining reducer cases for external (to the slice) actions.

    const authSlice = createSlice({
      name: "auth",
      initialState,
      reducers: {
        ...
      },
      extraReducers: builder => {
        builder
          .addCase(authError, resetAuth)
          .addCase(loginFail, resetAuth)
          // ... and so on
      },
    });
    

    In the above I'm assuming that all the actions are Redux-Toolkit actions (e.g. created in other slices or using createAction). If they are "legacy" Redux actions, then I believe you'll likely need to specify the type property for the case, e.g. .addCase(authError.type, resetAuth).

    Furthermore, to make the code a little more DRY you can use a Matcher to reduce the amount of code you write.

    Example:

    import { isAnyOf } from '@reduxjs/toolkit';
    
    const authSlice = createSlice({
      name: "auth",
      initialState,
      reducers: {
        ...
      },
      extraReducers: builder => {
        builder
          .addMatcher(isAnyOf(authError, loginFail, /* ... and so on */), resetAuth);
      },
    });