If you look at Hyperapp examples or even simplest counter example, you will see how it deals with state updates. Here is a typical example - incrementing and decrementing a counter.
Hyperapp is nice - it passes the complete state object to every action function, allowing every action to be pure if it wants to be. But the state object is the single source of truth, which means that sometimes actions have to work with properties deep inside the object. Even the above example - there is a lot of boilerplate in dealing with the count
property.
1 | const actions = { |
Every action has to get the desired property, compute its new value and return the new state object (or at least changed key). Here is how we can simplify this code using functional lenses implemented in Ramda library.
First, notice that up
action for example always works with property count
of an object passed to it. We can create a lens that will "look" at the property count
using R.lensProp
function.
1 | import {lensProp} from 'ramda' |
If we have a lens, and we know how we want to change the value, we can create new function that will be ready to increment the count
property.
1 | import {lensProp, over} from 'ramda' |
Of course, Ramda comes with increment function R.inc
, so we don't have to write one ourselves
1 | import {lensProp, over, inc} from 'ramda' |
Function incrementCount
is waiting for an object to be passed in. That object should have count
property, and it better be a number!
1 | const incrementCount = over(lensProp('count'), inc) |
Nice! We can create a "decrement count" function similarly. We can even reuse the same over(lensProp('count'))
functions, thanks to all Ramda methods being curried. My final code looks tight and reads almost English-like.
1 | import { lensProp, over, inc, dec } from 'ramda' |
And that's how you can clean up actions mutating state in Hyperapp. You can find the change to my counter example in commit bahmutov/hyperapp-counter-jsx-example/commit/0e2915