All Articles

Redux Let’s Refactor Our Higher-Order “Duck”

Let’s refactor a birdbath.

My last article described how a higher-order List “Duck” might be written. Here’s the code in 4 screenshots.

Remember we prefixed our actions with the name parameter. This helps prevent collision among other List Ducks.

Action Types

Action Creators

Reducer

Our higher-order Duck does its job well, but the reducer’s verbose! It could use some refactoring.

createReducer

createReducer is a helper function from the Redux Docs. It lets you easily spawn reducers by passing initial state and an object describing how the reducer responds to a given action.

It looks like this:

It takes two parameters: initialState and handlers.

initialState’s obvious, I hope — it’s our reducer’s initial state. 😏

handlers is more interesting — it represents your reducer’s response to a given action.

This might look scary but let’s break it down.

Line 2 returns a new reducer. That’s right, it spits out a reducer, preloading its state with your supplied initialState.

Line 3 uses an if/else to replace whatever switch cases your reducer would’ve had. It checks if your handlers object has the action’s type as a property.

If it does, invoke that property with state and action. Else, just return state.

Imagine we needed a counter reducer. Most of us would write this:

This is perfectly fine. But what if we plug it into createReducer?

Oh that is awesome! Concise, yet expressive. And it behaves the same way.

Refactoring our reducer from the last article may help us appreciate this even more.

Here’s the original reducer code, to recap:

The actionTypes.reset case is the easiest to refactor, so let’s begin with that.

Recall that createReducer passes the state and action to each of handlers functions. We don’t care, though. If someone fires the reset action return an empty array. The previous state and action details don’t matter.

Let’s cut those out.

I’m using computed property names to dynamically set actionTypes.reset as a key in our handlers object. I wrote a post covering them here. Basically it evaluates actionTypes.reset as ‘myListRESET’ because we originally created our Duck like so: makeListDuck('myList').

‘myListRESET’ then becomes a key on our handlers object and we set its value to a function that returns initialState. In the end, it looks like this:

{
  myListRESET: () => initialState;
}

Up next, actionTypes.addOne.

If someone fires the addOne action, we expect an item attached to it. We can also destructure the action to target its item.

I personally prefer this but either syntax works.

Now we’ll write actionTypes.addMany–same idea as addOne.

We’re on a roll, let’s do removeOne.

Remember from the last article, findItemById takes an id and returns a predicate comparing id to item.id.

For removeOne, when this predicate returns true (there’s a match), remove that item from the list.

In a similar spirit, here’s updateOne.

When the predicate returns true, return newItem. Else, return the normal item.

Last but not least, here’s set.

Return whatever action.items is. Easy-peasy!

I don’t know about you, but I think this reducer looks clean. And the functionality’s identical.