How to split one Redux reducer into multiple smaller ones

I’m very new to React & Redux and am working on the authentication portion of my app.

The way my backend API works is, once a user signs in, the response contains:

  • The JWT token to use for future requests
  • An object with the User’s data

What I would like to do is have the following occur:

  1. Store the token in localstorage
  2. Set a variable isAuthenticated in Redux to true (false for logout)
  3. Store the user information in a second Redux variable called user

I’ve currently the following Action:

import { SIGN_IN, SIGN_OUT } from "./Types";

export const signIn = response => {
    return {
        type: SIGN_IN,
        payload: response
    };
}

export const signOut = () => {
    return {
        type: SIGN_OUT
    };
}

and the following Reducer:

import { SIGN_IN, SIGN_OUT } from './Types';

const INITIAL_STATE = {
    isAuthenticated: null,
    user: {}
};

const authReducer = (state = INITIAL_STATE, action) => {
    switch(action.type) {
        case SIGN_IN:
            // This logic was removed thanks to comment from Sanish Joseph
            // localStorage.setItem("token", action.payload.token);

            return {...state, isAuthenticated: true, user: action.payload.user};
        case SIGN_OUT:
            // This logic was removed thanks to comment from Sanish Joseph
            // localStorage.removeItem("token");

            return {...state, isAuthenticated: false, user: {}};
        default:
            return state;
    };
};

export default authReducer;

and CombineReducers code:

export default combineReducers({
    ...
    auth: authReducer
});

This code works, but both user and isAuthenticated are children of auth (in other words, I have to get them both and refer to them via auth.user & auth.isAuthenticated.

What I don’t know how to do is how to write my reducer code so SIGN_IN will still send all the data I got from the API, but be able to create 2 separate pieces of state in Redux isAuthenticated & user.

Any help would really be appreciated!

Answer

So what I understood from the question is, you want to make isAuthenticated and user as 2 separate parts in the store.

For separating the state into multiple parts, you will need multiple reducers. Another important logic to remember is, all of your reducers will get called when any action is dispatched. It’s your decision whether to handle a particular action or not. Multiple reducers can handle same action.

So to create 2 parts one for auth and one for user, you can create 2 reducers. Remove user handling from auth reducer and add that to user reducer. Action is going to be the same. So in auth reducer,

import { SIGN_IN, SIGN_OUT } from './Types';

const INITIAL_STATE = {
    isAuthenticated: null,
    
};

const authReducer = (state = INITIAL_STATE, action) => {
    switch(action.type) {
        case SIGN_IN:           

            return {...state, isAuthenticated: true};
        case SIGN_OUT:          

            return {...state, isAuthenticated: false};
        default:
            return state;
    };
};

export default authReducer;

and your user reducer will look like,

import { SIGN_IN, SIGN_OUT } from './Types';

const INITIAL_STATE = {    
    user: {}
};

const userReducer = (state = INITIAL_STATE, action) => {
    switch(action.type) {
        case SIGN_IN:
            

            return {...state,  user: action.payload.user};
        case SIGN_OUT:
           

            return {...state, user: {}};
        default:
            return state;
    };
};

export default userReducer;

and don’t forget to combine them,

export default combineReducers({
    ...
    auth: authReducer,
user:userReducer
});