Please disable your adblocker / privacybadger. Otherwise the sample code is disabled…

Modern JavaScript environments have many ways of dealing with state. We can use closures or classes to have some shared state, but sometimes a more elaborate state management library is needed. One popular option is the Redux pattern.

Redux 101

If you’re familiar with Redux, you can skip this, but just to recap, here is a small overview of what Redux is.

Properties

There is one state object

The state object is immutable

A new version of that state object is made with every change

Changes happen by dispatching actions, resulting in the flow below

Flow (in a scenario where one would use thunk)

User clicks button

An action gets dispatched ( {type: LOGIN, data: {username: x, password: y}} ). An action is a plain object that contains a type and data .

). An action is a plain object that contains a and . Middleware listens for that action and makes an async request. Let’s assume it succeeds, then it will dispatch another action ( {type: LOGIN_SUCCESS, data: {accessToken: x, refreshToken: y}} )

) A reducer listens for that action and reduces the old state to a new version of the state. ( (state, action) => ({…state, someNewStateValues }) )

) The state is updated and the component can re-render with the new state.

The best thing about Redux is that it makes it really easy to reason about the state as all changes have to go through these steps. The biggest downside however, is that this requires quite a bit of boilerplate. Creating actions and reducers can be a lot of work, not to mention that for good measure, most of the UI state should now be contained in that one object. So even things like a checkbox or a toggle need to go through there instead of just mutating a variable locally or in a service.

The Problem

As mentioned above, the whole Redux setup requires quite a bit of boilerplate. Some of it can be reduced with the pointers on the redux website. But there is something to reduce it even more (and make it a bit clearer in the process).

Sometimes, the state object gets quite nested. Take for instance this object — in short, it describes a 3d scene with objects, and groups of objects.

Let’s say the user needs to be able to add extra objects to both the structure of the scene and to the groups within that structure. Those reducers may be written as follows:

While this is not necessarily the most optimized version of this code (we can use some other constants to make it more readable and have some functions to abstract away some object mutations), there is something else going on that is more tricky to solve.

The deeper the structure of the state, the more object spreading we need to do to keep everything intact and the more convoluted the code.

Luckily there is a solution.

Lenses

Lenses, and some functions to use in conjunction, provide a way to: view values, immutably set them, and apply functions over them. All within an object, at a certain path. It allows you to give the lens a full object (like a state) and get back the full object, but with the new value set or the function applied, no matter how deeply nested the value you wanted to mutate.

When looking at the documentation of Ramda (the library we’re using for its lenses implementation). There are 4 things that have to do with lenses to create them, of which we’ll use two, lensPath and lensProp . The lensPath function allows you to create a lens for a nested path while the lensProp function lets you create a lens for a specific property. The other two can be useful in some cases (especially lensIndex ), but they’re not too hard to deduce once you know these two. In our case, let’s make a lens for those objects and groups in our little scene.

If you are wondering about the compose , I’ve written an article about that.

With the lens specified, we can use three functions, view , set and over . These allow you to respectively view the value at the lens’ path for a given object, set a value, or run a function over the value. Like this:

The nicest thing about this is that we don’t have to manually spread the objects! Also, the over function makes it extremely powerful. So let’s rewrite our earlier reducers using lenses.

By splitting out the append functionality to a function so we can use over , we’ve been able to reduce the actual reducer itself to one line! Of course there are some lenses to define now, but this means the switch statement (or lookup object) for the actual reducer is way less cluttered. Lenses could even be imported from separate files. In the second one, the idea of using multiple lensProps and composing them becomes clear. Within the groups , there are objects , and as we have the lensProp for objects , we can re-use it there.

Concluding

Yes, redux comes with a certain overhead. Especially in circumstances where there is an extremely nested state. Naturally, trying to avoid this nesting is better in some cases. In other cases, not having that nesting may actually make it harder to reason about the object. In those cases, using lenses may be beneficial.