So you’ve screwed up your Redux store — or, why Redux makes refactoring easy

Emil Ong
Haus Engineering Blog
4 min readMay 18, 2016
Cropped from A jenga tower, by Guma89 is licensed under CC BY-SA 3.0

Redux is an amazing piece of technology, mainly because it implements one very straightforward pattern and then gets out of your way. Its simplicity lets you do at least two important things:

  1. Shoot yourself in the foot.
  2. Grow another foot.

Seriously though, a framework/tool’s ability to let you refactor easily should be considered a very attractive feature. A lot of software only supports shooting yourself in the foot. Redux is flexible enough to let you refactor easily and safely even if your first draft wasn’t perfect.

Unlike some other posts which just tell you best practices, this post will try to tell you some ways to recover if you didn’t follow those best practices.

Let’s get in trouble

Let’s imagine that we have a reducer that originally looked like this:

Our store just has a bunch of items in a list that we got from somewhere. Now imagine that we did not create DRY selectors and we have a bunch of selectors close to our container components:

In other words, we have a bunch of code strewn about the app that depends on the shape of the state.

The best practice here would really been to have created selectors and reducers in tandem, then reused the selectors for connected components.

We didn’t do that though, so now we’re in for a painful refactor and we have a feature that we need to add…

Redux actions are just events

One thing you may not know about Redux is that multiple reducers can handle the same action. I’m not sure if this was intentional or purposeful, but let’s take advantage of it nonetheless for refactoring. (I’ll also show another way to use this for normalization below.)

Multiple reducers can handle the same action in Redux

So let’s return to our example above. Let’s say that we want to store our items by their id instead of as a big list and forcing components that want a single item to find it. One option is that we could introduce a selector and start using it on the existing store state, gradually moving our old components over to this selector.

This is better and probably what we should have done in the first place, but our state is still stored in an inefficient way, at least if we have a lot of items and request them primarily by id. We can restructure our store, taking advantage of the fact that multiple reducers can handle the same action.

We maintain the consistent selector API here, but switch the internal representation to something with perhaps better performance on large item lists. At the same time, we can leave the original items reducer and its state tree in place which maintains backwards compatibility for other parts of our app. Both reducers receive the same action, but alter their portion of the state in different ways.

Peeking at denormalized data

We can do more than just refactor with Redux’s multiple-reducer-action-handling feature though!

Sometimes, you have a big reducer that seems to have a bunch of potentially splittable subparts, but there are a few dependencies in between them that make it difficult to find an obvious way to split them. This might happen with nested data in particular.

Our literary reducer above is updating both books and authors on every action. (BTW, you should checkout normalizr if this comes up for you a lot.) This approach is very reasonable and in this small example, it’s fairly straightforward, but there’s another way that you could structure this with multiple reducers handling the same actions:

In this case, we ended up with a bit more code, but there is a nice aspect of each reducer action handler being responsible for exactly one type of update. I’d love to hear of other folks using this pattern.

Summary

Wrapping up, let me just leave you with a few points:

  1. Try to colocate your selectors and reducers. Selectors are your reading API for your components to access your store.
  2. If you structure your store badly the first time, Redux gives you the chance to recover. (Don’t forget to do #1 if you didn’t the first time!)
  3. You can use actions multiple times to structure your store however you want, including for refactoring and normalization.

If you’re interested in working with us, discussing issues like these and others, please check out our jobs page and get in touch!

--

--