Code your own combineReducers
Getting tired of following yet another tutorial on how to use Redux?
It’s time for a completely different approach. To master Redux, we will code it from scratch. Today it’s time for the combineReducer function.
What is a reducer
Before we start, we need a quick reminder what a reducer is.
A reducer is a function that takes the Redux state and a Redux Action as input parameters. It uses the input parameters to generate a new state that the function returns.
An example of a simple reducer looks like this:
function counter(state = 0, action) {
switch (action.type) {
case "INCREMENT":
return state + 1
case "DECREMENT":
return state - 1
default:
return state
}
}
As you can see the reducer function is basically a long switch.
When the application grows with more and more actions, the reducer function grows with it. We want to avoid large functions because they are difficult to overview, extend and combine.
There is a function called combineReducer that is shipped with Redux. It lets us split the reducer up to smaller reducers and combine them so we can use them in Redux.
Let’s code our own combineReducer!
We will start by copying the example usage from the official docs:
// reducers.js
export default theDefaultReducer = (state = 0, action) => state
export const firstNamedReducer = (state = 1, action) => state
export const secondNamedReducer = (state = 2, action) => state
// Use ES6 object literal shorthand syntax to define the object shape
const rootReducer = combineReducers({
theDefaultReducer,
firstNamedReducer,
secondNamedReducer,
})
const store = createStore(rootReducer)
console.log(store.getState())
// {theDefaultReducer : 0, firstNamedReducer : 1, secondNamedReducer : 2}
We are going to use this as a starting point for our own implementation.
Function definition
The combineReducer function call takes an object with reducers. Let’s start by implementing support for that.
function combineReducers(reducers) {}
We now have the function combineReducers that accepts reducers as input parameters. Sweet!
Return a new empty reducer
Look at the example again. combineReducer returns a rootReducer that is used as an argument to createStore. In other words, the rootReducer works just like a normal Redux reducer.
As we learned in the beginning in this article, a normal Redux reducer is a function that takes two parameters: state and action. Let’s return that.
function combineReducers(reducers) {
return function combination(state = {}, action) {}
}
Return a reducer that creates a new state
In the example, there is a console.log of the state of the store. We can see the expected output in the comments. The output after running getState should be an object with all the reducer names as keys and the state as the value.
Let’s start by returning an object with the reducer names as keys. To simplify we will have a hardcoded string as values.
function combineReducers(reducers) {
// First get an array with all the keys of the reducers (the reducer names)
const reducerKeys = Object.keys(reducers)
return function combination(state = {}, action) {
// This is the object we are going to return.
const nextState = {}
// Loop through all the reducer keys
for (let i = 0; i < reducerKeys.length; i++) {
// Get the current key name
const key = reducerKeys[i]
nextState[key] = "Here is where the state will be"
}
return nextState
}
}
When we run this code we will get this output:
// {theDefaultReducer : "Here is where the state will be", firstNamedReducer : "Here is where the state will be", secondNamedReducer : "Here is where the state will be"}
We are almost there! The final thing we need to do is to print the object with the correct values.
Return a reducer that returns correct state
Let’s finish this up by adding the correct state of each sub reducer as value to the printed object. To do that we will call each sub reducer.
function combineReducers(reducers) {
// First get an array with all the keys of the reducers (the reducer names)
const reducerKeys = Object.keys(reducers)
return function combination(state = {}, action) {
// This is the object we are going to return.
const nextState = {}
// Loop through all the reducer keys
for (let i = 0; i < reducerKeys.length; i++) {
// Get the current key name
const key = reducerKeys[i]
// Get the current reducer
const reducer = reducers[key]
// Get the the previous state
const previousStateForKey = state[key]
// Get the next state by running the reducer
const nextStateForKey = reducer(previousStateForKey, action)
// Update the new state for the current reducer
nextState[key] = nextStateForKey
}
return nextState
}
}
Now we are done! We have written the whole combineReducers function. Not that complicated, was it?
Take a look at the “real” combineReducer function and compare (it’s the function at the bottom). The real one has more error handling and some optimizations.
But the core of it is the same as the one we just created!
Next step
Learn about the source code of Redux Thunk in the next post: Async Actions with Redux Thunk demystified