Reputation: 2576
Maybe I'm missing something completely obvious but this has been tripping me up today.
Let's say we have a Redux store with a structure like so:
const state = {
...
pages: {
...
accountPage: {
currentTab: 'dashboard',
fetching: false,
tableSettings: {
sortDir: 'asc',
sortField: 'name'
}
}
}
}
So there is obviously a main reducer...
export default combineReducers({
...
pages: pagesReducer
...
});
Then the reducer for pages has the reducer for each page...
export default combineReducers({
...
accountPage: accountPageReducer
...
});
And now finally we get down to the meat of the problem, the reducer for this particular piece of state.
export default handleActions({
[setCurrentTab]: (state, action) => { ... },
[setIsFetching]: (state, action) => { ... }
});
That's all good right? Well, the key in the state given at the outset at tableSettings
should actually be handled by it's own reducer. This pattern may exist many times in the state, so it is abstracted away to a reducer-creating function:
const defaultState = {
sortDir: 'asc',
sortField: null
};
export const createTableSettingReducer (actions, extra ={}) => {
return handleActions({
[actions.changeSortDir]: (state, action) => ({ ...state, sortDir: action.payload }),
[actions.changeSortField]: (state, action) => ({ ...state, sortField: action.payload }),
...extra
}, defaultState)
}
So, above the reducer for the sections of state (accountPageReducer
), we created the reducer:
// pretend these actions were imported
const tableSettingsReducer = createTableSettingReducer({
changeSortDir: setSortDir,
changeSortField: setSortField
});
So the question is, where do I put tableSettingsReducer
?
This of course, doesn't work:
export default handleActions({
[setCurrentTab]: (state, action) => { ... },
[setIsFetching]: (state, action) => { ... },
tableSettings: tableSettingsReducer
});
It doesn't work because handleActions
expects to use the action constants as keys, not the actual key in the state.
There is also nowhere to use combineReducers
, since there is only one nested reducer of this slice of state. currentTab
and fetching
do not need their own reducer, so it's fruitless to use combineReducers
.
I know that recently redux-actions
started support nested reducers...but there isn't really any documentation available showing exactly how it's supposed to be done, or even describing the parameters needed to make it happen.
I could possibly use combineActions
, and combine all of the actions in handleActions
for every action that can be taken by a nested reducer. But that doesn't seem very clean...plus, what if the nested reducer has it's own nested reducers? That means every time those reducers can process a new action, that action needs to be added to combineActions
in all its parents. Not the best.
Thoughts?
Upvotes: 0
Views: 1056
Reputation: 735
let say you have the initialState = { data : []}
let assume that the upcoming action has payload of an array
export the reducer as the following :
return handleActions({
["Action Type 1" ]: (state, { payload }) => {
return { ...state, data: [...state.data, ...payload ]} ;
},
["Action Type 1" ]: (state, { payload }) => {
return { ...state, data: [...state.data, ...payload ]} ;
},
}, initialSate );
import this reducer in your combine reducer .
Upvotes: 0
Reputation: 66
Every key in your state gets its own reducer. Some reducers are really simple, some are themselves composed of other reducers. All the sister keys at each level of your state tree can be combined with combineReducers
.
const initialCurrentTab = 'dashboard';
const currentTabReducer = handleActions({
[setCurrentTab]: (state, action) => {
return action.payload;
},
}, initialCurrentTab);
const defaultFetchingState = false;
const fetchingReducer = handleActions({
[setIsFetching]: (state, action) => {
return action.payload;
},
}, defaultFetchingState);
export default combineReducers({
currentTab: currentTabReducer,
fetching: fetchingReducer,
tableSettings: tableSettingsReducer,
});
Upvotes: 2