Leverage Redux State Selectors

The biggest aspect of working with Redux is the structure of your state. It can make or break an application. An overly complex and highly duplicated state can end up slowing down/bogging down your application. Not necessarily in noticeable runtime decreases but a decrease in the speed at which you can find and fix bugs or add new features.

There are a lot of tools that make working with state a little bit more manageable, some packaged with Redux itself, like combineReducers which allows you to divide your state up in chunks of tightly controlled "units." The reducers associated to the parts have less state to maintain, and given the nature of reducers/state you can creatively nest uses of combineReducers and reducer chains to achieve a nice method of organizing the code that will operate on the monster that has become your application state.

No matter what you do, your state will become unwieldy, and that's becuase you can't know all of your requirements ahead of time. As new features/bug fixes come in new aspects of state will be added. Prehaps now one piece of state is pretty isolated to a small part of the application -- but months down the road another part needs it. And after you've managed to patch that into place further down the line a new feature needs it. For the most part, these problems can be hidden if you're using something like react-redux's connect API, but those can get pretty ugly when you're fetching state from all over the place.

const mapStateToProps = ({
  tenant,
  adminView: {
    templateApi: { templateList: { selectList }, fetching },
    preferenceListApi: { preferenceList },
  },
}) => ({...});

That's not too bad, and some of the issues that causes this is not decided early on if you're going to commit to redux-promise-middleware or redux-thunk -- but I digress. In this particiluar bit of code, templateList is used in a few different places. It might be useful to only fetch those once per tenant. Well, that's hard. We ended up building React on top of an existing Rails app on a short timeframe and for some reason our lead engineer on the project decided the best approach was just to mimic the Rails pattern of "request equals view equals content," so we ended up just rendering our pages on the client side in React instead of on the Rails app server (which is still an improvement, whew...).

That led to the problem of having "view" keys polluting our state, and having shared bits of data hidden between them. For example, the user show and edit "pages" don't share a user record. Mostly because of a hard refresh between them but even without one of them is tied to the userShowView state field and the other to the userEditView state key. I'm in no way endorsing this design pattern, but it's one that can happen in a real situation and after you've completed a full pass on the app and launched it state changes become quite difficult. If I wanted to unify the usage of the templateListApi from the example above I would be forced into tracking down every single reference and correctly pointing it to the new location. This is error prone. Here I've accessed it via destructuring, perhaps another teammate accessed it by digging in from the top level state and perhaps another referenced a parent state field in a destructuring statement and then dug in to the properties in the function body -- point is, there are a lot of possible ways those state fields can be accessed and in different places too (as I said, we use redux-thunk so some action creators feature some usage of getState).

But there is a simple solution. Selectors. I know, the name can be easily confused with some terminology from CSS -- so I prefere "state selectors" instead. I didn't make the name up, honestly I read it about it somewhere and instead of linking there I'm writing an article so I can claim some partial glory (but no, the original article was pretty general and I'm talking about real world usage).

So what is a state selector? Well that's easy. It's a function. That's it. It takes state as an input, and outputs some state field that you desire. Maybe you have a selector for getCurrentUser, getUser, getTemplateList whatever. If you need data from redux state, you have a state selector that gets it for you.

Why is this great? Well, imagine if I wrote:

const getCurrentUser = state => state.server.authenticationApi.user;

(Don't ask why you'd nest it this way, this is a contrived example).

And at some later point, your suddenly realize this is bad. And you have a bunch of views that depend on getting this user data. But you were smart, you used the state selector instead of accessing it manually. So what does it take to change the state structure? Well...

const getCurrentUser = state => state.currentUser;

And... that's it. This would work if I had one component, ten, or even one hundred. It doesn't matter. If I diligently use state selectors when getting information off of the state, then updating state structure means updating a selector and the application doesn't care. Selectors can also depend on other selectors, if you want to build complex selectors. You can even encapsulate commonly done operations like pre-processing state fields for a specific use case, etc...

After introducing state selectors our mapStateToProps simplified significantly. This allowed us to transform this:

const mapStateToProps = ({
  tenant,
  adminView: {
    templateApi: { templateList: { selectList }, fetching },
    preferenceListApi: { preferenceList },
  },
}) => ({
  tenant,
  selectList,
  fetching,
  preferenceList,
});

Into this:

const mapStateToProps = (state) => ({
  tenant: getTenant(state),
  selectList: getTemplateSelectList(state),
  fetching: getTemplateListFetching(state),
  preferenceList: getPreferenceList(state),
});

I definitely prefer the second. It's shorter, more concice, easier to read quickly especially if I start writing mapDispatchToProps and maybe a mergeProps nearby.

The benefits of "state selectors" should be clear. They can clean up and simplify messy bits of working with state as well as make it dead simple to roll out updates that include changes to the structure of state. If you're lucky and get to use state selectors fresh out of the gate then your state gets to evolve with your requirements allowing it to stay clean and efficient. Despite being such a minor tool in the Redux toolbelt, it's probably one of the most powerful. As a means of totally decoupling any part of your application with the initial decisions you were forced to make about state structure.

P.S.

As a side note, redux-saga has direct support for this pattern by using it's select effect which takes in a state selector and returns the associated state value. So now not only do you have clean mapStateToProps for your connect calls but you also get the ability to freely alter your state strucutre and have that much less hassle integrating redux-saga into your application.

As a bit of an expansion on this idea and more, the next post will be about redux-saga and how it compares to other libraries of it's kind.