Nested redux reducers

It is perfectly fine to combine your nested reducers using combineReducers. But there is another pattern which is really handy: nested reducers.

const initialState = {
  user: null,
  organisation: null,
  token: null,
  cypher: null,
  someKey: null,
}

function authReducer(state = initialState, action) {
  switch (action.type) {
    case SET_ORGANISATION:
      return {...state, organisation: organisationReducer(state.organisation, action)}

    case SET_USER:
      return {...state, user: userReducer(state.user, action)}

    case SET_TOKEN:
      return {...state, token: action.token}

    default:
      return state
  }
}

In the above example, the authReducer can forward the action to organisationReducer and userReducer to update some part of its state.


Just wanted to elaborate a bit on the very good answer @Florent gave and point out that you can also structure your app a bit differently to achieve nested reducers, by having your root reducer be combined from reducers that are also combined reducers

For example

// src/reducers/index.js
import { combineReducers } from "redux";
import auth from "./auth";
import posts from "./posts";
import pages from "./pages";
import widgets from "./widgets";

export default combineReducers({
  auth,
  posts,
  pages,
  widgets
});

// src/reducers/auth/index.js
// note src/reducers/auth is instead a directory 
import { combineReducers } from "redux";
import organization from "./organization";
import user from "./user";
import security from "./security"; 

export default combineReducers({
  user,
  organization,
  security
});

this assumes a bit different of a state structure. Instead, like so:

{
    auth: {
        user: {
            firstName: 'Foo',
            lastName: 'bar',
        }
        organisation: {
            name: 'Foo Bar Co.'
            phone: '1800-123-123',
        },
        security: {
            token: 123123123,
            cypher: '256',
            someKey: 123
        }
    },
    ...
}

@Florent's approach would likely be better if you're unable to change the state structure, however


Inspired by @florent's answer, I found that you could also try this. Not necessarily better than his answer, but i think it's a bit more elegant.

function userReducer(state={}, action) {
    switch (action.type) {
    case SET_USERNAME:
      state.name = action.name;
      return state;
    default:
      return state;
  }
} 

function authReducer(state = {
  token: null,
  cypher: null,
  someKey: null,
}, action) {
  switch (action.type) {
    case SET_TOKEN:
      return {...state, token: action.token}
    default:
      // note: since state doesn't have "user",
      // so it will return undefined when you access it.
      // this will allow you to use default value from actually reducer.
      return {...state, user: userReducer(state.user, action)}
  }
}