Seeing Through Redux with Lenses

Using functional programming to manage your state

To refresh our memories, a basic reducer in redux is

export default (state, action) => state

Some function that takes in the previous state object and an incoming action object and the result of that function will be the new state for our application.

Using this paradigm of a single reducer quickly leads to code that is hard to read but more importantly harder to update:

Each time we update a piece of state, we have to walk the whole object, copying over references to everything leading to and everything after the updated value. On top of that, we have to check to see which switch statement should be called. This reducer is just toooo smart.

Luckily for us, redux offers combineReducers , which lets us instead create namespaced slices of state like users and comments and operate on those slices of state in more isolated pieces:

combineReducers still returns a function of signature (state, action) -> state , but it does some plumbing for us to make sure that when we call an action that hits on the userReducer , we give that function the slice of state called users .

That means for every action that the reducer decides to react to, it is responsible for returning the slice of state users , having to copy over the entire previous state leading to the update value and all of the values after it. It still suffers from the same issues as the first, but we have just namespaced the issue. An improvement but not ideal.

If It Ain’t Broke

Using the above paradigm works and helps reduce the complexity of our state tremendously. Using the above with reselect limits the exposure to the reducer logic and even lets us decouple the reducer logic and result from how the UI consumes that data.

Decoupling the UI from the state shape means that as long as the contract for the selector is kept up to date with changes to UI needs and API responses, You could refactor one without ever affecting the other. Using selectors , we have created a basic contract with redux to give us a slice of shape ( or a massaging of slices of state ).

selectors are functions that, given state , return a piece of state. Since they are just state -> value functions, they can literally do anything. Well, anything that isn’t helping you set that same value. All selectors can do is select information, not help create it.

What if there was a way to create a selector that could not only select or get information from the state regardless of shape ( creating a contract between services ) but also update or set information regardless of shape using the same contract? What if we could decouple our reducers in the same way we decoupled our selectors ?

Enter Lenses

In a very abstract sense, a lens is a function that points to a specific part in a data structure. Suppose that we had the data structure:

{ user: { name: string } }

We could create a lens that points to name . That would mean if we view the lens given the following object:

{ user: { name: 'Tim' } }

it would resolve into the value of 'Tim' . Very much like a selector where we describe a way to get a part of a shape. However, unlike selectors , we can use lenses to set a value as well:

const data = { user: { name: 'Tim' } }

const updatedData = R.set(nameLens, 'John', data)

// { user: { name: 'Joh' } }

You can read more about lenses by the amazing Randy here. For our purposes, they are just like selectors except they allow us to set values as well as get them.

Your Point Is?

If we use lenses for our selector logic, we gain the decoupling of our reducer and our state shape for free:

We have a file called lenses that has a view to the root of our state object. We also import a function called set from a future npm package ( demo repo can be found here ).

Using the above syntax, these setters allow us to describe in an a declarative way what we want to do ( set ), where we want to do it ( root ), and how we want to find that value ( with ).

How do we use lenses to get or select the values? A basic example is as simple as using R.view :

Now we can use it inside of mapStateToProps

state => ({ ids: get.account.ids(state) }) // current account.ids

What if we have a complex data structure that we need to get more than just one single lens? Our getters are just state -> value functions so they can be as complex as need be: