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.jsimport { 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)} }}