Redux: Implementing combineReducers() from Scratch

Dan Abramov
InstructorDan Abramov

Share this video with your friends

Send Tweet
Published 6 years ago
Updated 9 months ago

Learn how to build a reasonable approximation of the combineReducers() utility in 15 lines. No magic!

[00:00] In the previous lesson, we learned to use the combine reducer function, which comes with Redux and generates one reducer from several other reducers, delegating to them paths of the state tree.

[00:14] To gain a deeper understanding of how exactly combined reducers works, we will implement it from scratch in this lesson.

[00:22] Combine reducers is a function, so I'm writing a function declaration. Its only argument is the mapping between the state keys and the reducers, so I'm just going to call it Reducers.

[00:37] The return value is supposed to be a reducer itself, so this is a function that returns another function. The signature of the return function is a reducer signature. It has the state and the action.

[00:53] Now, I'm calling the object key's method, which gives me all the keys of the reducer's object. In our example, this is todos and the visibility filter.

[01:05] Next, I'm calling the reduce method on the keys, because I want to produce a single value, such as the next state, by accumulating over every reducer key and calling the corresponding reducer.

[01:19] Each reducer passed through the combine reducers function is only responsible for updating a part of the state. This is why I'm saying that the next state by the given key can be calculated by calling the corresponding reducer by the given key with the current state by the given key and the action.

[01:46] The [inaudible] reduce wants me to return the next accumulated value from the call back, so I'm returning the next state. I'm also specifying an empty object as the initial next state, before all the keys are processed.

[02:02] There we have it. This is a working reimplementation of combined reducers utility from Redux.

[02:12] Let's briefly recap how it works. I'm calling combined reducers with an object whose values are the reducer functions and keys are the state field they manage. Inside the generated reducer, I'm retrieving all the keys of the reducers I passed to combine reducers, which is an array of strings, todos and visibility filter.

[02:36] I'm starting with an empty object for my next state and I'm using the reduce operation of these keys to fill it gradually.

[02:46] Notice that I'm mutating the next state object on every iteration. This is not a problem, because it is the object I created inside the reducer. It is not something passed from outside, so reducer stays a pure function.

[03:01] To calculate the next state for a given key, it calls the corresponding reducer function, such as todos or visibility filter.

[03:12] The generated reducer will pass through the child reducer only if part of its state by the key. If its state is a single object, it's only going to pass the relevant part, such as todos or visibility filter, depending on the current key, and save the result in the next state by the same key.

[03:34] Finally, we use the array reduce operation with the empty object as the initial next state, that is being filled on every iteration until it is the return value of the whole reduce operation.

[03:50] In this lesson, you learned how to implement the combined reducers utility that comes with Redux from scratch.

[03:58] It is not essential to use in Redux, so it is fine if you don't fully understand how it works yet. However, it is a good idea to practice functional programming and understand functions can take other functions as arguments and return other functions, because knowing this will help you get more productive in Redux in the long term.

Sequoia McDowell
Sequoia McDowell
~ 6 years ago

I love that you explained what combineReducers does long-hand (writing it out explicitly) in the last video before introducing it here. That took the magic out of it and made it really easy to understand what's going on. Kudos!

mobility-team
mobility-team
~ 5 years ago

Great explanation, great content!

Enrique
Enrique
~ 5 years ago

That was so meta, it made me go O_o

Brad Tittle
Brad Tittle
~ 5 years ago

Am I correct in saying the following:

Every event is dispatched to all of the reducers in combined reducers. If the action.type doesn't exist in the reducer it doesn't happen.

Or

If I wanted to have two different things happen with the same action, I can have to separate updates happen to the state. Let's say I am rolling the dice and tracking all rolls of the dice, I might keep a currentRoll running for display and a rollHistory for a fairness tracker. Since all actions pass through all reducers, I don't need to worry about managing which reducer is getting the action.

The more I look at this the more brilliant it is. The problem is attempting to pass it on to the next person.

I once got into an argument with someone about the ability of javascript to do multi dimensional arrays. He was fixated on the form array[ ][ ][ ][ ]. From what I can tell, he is correct that javascript doesn't do that. What is the difference between that and array[ { [ { [ ] } ] } ] and array [ ] [ ] [ ] [ ]. I think there are many differences. The question is, can I make the first one accomplish the tasks that the second one use to do. If n is small, it isn't that terrible. When n starts getting big, n^2 starts beating me up. But for most of the times I have run into multiple dimensions on arrays, the j, k and l usually stay small.

That is me attempting to say that communicating programming ideas to programmers can be challenging. Trying to communicate programming ideas to non programmers and have them actually hear what you said... That is how folks end up needing medication.

dan entous
dan entous
~ 4 years ago

noticed that using combineReducers “from scratch” vs redux has a slight observable difference:

  • “from scratch” initialises by calling each reducer once
  • redux initialises by calling each reducer twice
  • then the store.createStore( todoApp ); calls each reducer again, once

why is that?

Enoh Barbu
Enoh Barbu
~ 4 years ago
Aaron
Aaron
~ 4 years ago

This (and this entire lesson) is really very good, thank you!