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)}
}
}