1. 13
    Redux: Reducer Composition with Arrays
    2m 21s

Redux: Reducer Composition with Arrays

Dan Abramov
InstructorDan Abramov
Share this video with your friends

Social Share Links

Send Tweet
Published 8 years ago
Updated 3 years ago

Learn the fundamental pattern of building maintainable Redux applications: the reducer composition, and how it can be used to update items in an array.

[00:00] In the previous lesson we created a reducer that can handle two actions, adding a new to-do, and toggling an existing to-do. Right now, the code to update the to-do item or to create a new one is placed right inside of the to-dos reducer.

[00:17] This function is hard to understand because it makes us two different concerns, how the to-do's array is updated, and how individual to-dos are updated. This is not a problem unique to Redux. Any time a function does too many things, you want to extract other functions from it, and call them so that every function only addresses a single concern.

[00:41] In this case, I decided that creating and updating a to-do in response to an action is a separate operation, and needs to be handled by a separate function called to-do. As a matter of convention, I decided that it should also accept two arguments, the current trait and the action being dispatched, and it should return the next trait.

[01:05] But in this case, this trait refers to the individual to-do, and not to the least of to-dos. Finally, there is no magic in Redux to make it work. We extracted the to-do reducer from the to-dos reducer, so now we need to call it for every to-do, and assemble the results into an array.

[01:28] While this is not required in this particular example, I suggest that you always have the default case where you return the current trait to avoid all [inaudible] in the future. The part described in this lesson is pervasive in Redux's development, and is called reducer composition.

[01:45] Different reducers specify how different parts of the trait tree are updated in response to actions. Reducers are also normal JavaScript functions, so they can call other reducers to delegate and abstract a way of handling of updates of some parts of this tree they manage.

[02:05] This pattern can be applied many times, and while there is still a single top level reducer managing the state of your app, you will find it convenient to express it as many reducers call on each other, each contribution to a part of the applications trait tree.

Demetri Ganoff
Demetri Ganoff
~ 8 years ago

It seems like when you separated the logic for "ADD_TODO" and "TOGGLE_TODO", there was some duplication of logic with the switch statements. Wouldn't it be easier to have separate JavaScript functions for each action type? This way they wouldn't be concerned with the action string and only handle the logic of creating the new state.

Dan Abramov
Dan Abramovinstructor
~ 8 years ago

What duplication are you referring to specifically? I don’t see it in the end result. That said, you can definitely make functions even more granular if you’d like to.

Demetri Ganoff
Demetri Ganoff
~ 8 years ago

The switch statement in the todo and todos functions have the same logic. Instead, if you create a addTodo and toggleTodo function that only handles returning the state for those actions, you can eliminate their concern with the action. Of course, this is no longer composing reducers, but separating functionality into easier-to-read chunks.

Dan Abramov
Dan Abramovinstructor
~ 8 years ago

I’m probably missing your point. Aside from having a switch with the same constants, I don’t see any duplication in their logic. The todos reducer handles changes in array, and todo reducer handles changes to individual todo. There is no shared code between them, again, except for the switch statement. As the logic gets more complicated (more actions are handled), todo reducer will grow, but todos will stay pretty much the same. That’s the point of separating them. If they’re not separated, adding more actions that change individual todos will require copy-pasting code for changing todo at an index—now that would be duplication. If you don’t agree please provide some code examples of the duplication you’re referring to (please mark duplicated logic with comments), and how you suggest to reduce that duplication.

Joel Hooks
Joel Hooks
~ 8 years ago

It seems like when you separated the logic for "ADD_TODO" and "TOGGLE_TODO", there was some duplication of logic with the switch statements. Wouldn't it be easier to have separate JavaScript functions for each action type? This way they wouldn't be concerned with the action string and only handle the logic of creating the new state.

When I'm removing duplication, I like to think of it in more functional terms. I don't want to have separate functions for action types, but instead want to provide pure functions that return the new data. I didn't see the logic duplication you are talking about in this case. You can clone JSBins easily and paste a link if you have some ideas for improvement. That'd be great!

At the end of the day, definitely remove duplicate logic with pure functions anytime it makes sense!

Dan Abramov
Dan Abramovinstructor
~ 8 years ago

Here’s a different version, with createReducer abstracted away. This removes the switch statement, and I hope, makes it clear there is no duplication between todos and todo:

https://jsbin.com/yezuramoxi/1/edit?js,console

Demetri Ganoff
Demetri Ganoff
~ 8 years ago

Thanks for the responses! I agree that the only real logic duplication is in the switch statements. Here's an example of what I was envisioning: https://jsbin.com/rosuvobate/1/edit?html,js,console

Please let me know if this is acceptable within the best practices of Redux. Thanks!

Dan Abramov
Dan Abramovinstructor
~ 8 years ago

This is acceptable, but when you add more actions that change a single todo (e.g. EDIT_TODO) you’ll find yourself duplicating the logic to map over the array and change a single item. :-)

There are no strict rules here. Do whatever makes sense. However I think you’re mistaken when you say switch statements are duplication. In a large app, you’ll have a switch statement inside every reducer, and some of them will handle the same actions. This is normal—not something to abstract away. With the approach you’re suggesting, you’re trading a visual duplication (switch statements) for logical duplication (code to update a single item in an array). Anyway, it’s up to you. And this example is small enough that it’s hard to agree on a single best practice—it can be done in a variety of ways all of which are fine.

Demetri Ganoff
Demetri Ganoff
~ 8 years ago

I see what you mean about the map duplication when adding more actions. I definitely what to keep my code scalable and free from logical duplication.

Thanks again and great job on the videos!

Abe Massry
Abe Massry
~ 8 years ago

Transcript says: you return the current trait to avoid all [inaudible 1:36] in the future Should be: you return the current state to avoid odd bugs in the future

Enoh Barbu
Enoh Barbu
~ 6 years ago
Patrick Akpala
Patrick Akpala
~ 6 years ago

i know this is probably 2 years late but why does passing undefined in the todo allow the test to pass ? is this because the creation of the reducer function that was abstracted doesn't know the initial state for the ADD_TODO action?

would it be wrong to pass an empty array into the todo function that gets called in the ADD_TODO?

thanks for your reply

Eleni Lixourioti
Eleni Lixourioti
~ 6 years ago

@Patrick and anyone with the same question of "why do we pass undefined to todo():

The state argument in the context of the todo() function refers to the state of a single todo item. In the case where we're adding a todo item ('ADD_TODO'), there isn't a previously existing todo item to pass a previous state of, as we're only just creating it.

Therefor the reducer takes "nothing" as a state (represented by undefined) and creates "something" as the new state (a new todo object).

Markdown supported.
Become a member to join the discussionEnroll Today