jesseskinner / hover

A very lightweight data store with action reducers and state change listeners.
MIT License
98 stars 7 forks source link

Composing actions #19

Closed jesseskinner closed 8 years ago

jesseskinner commented 8 years ago

After using Hoverboard.compose for a while, I've tried different ways of composing the actions. Hoverboard.compose only composes state data, and actions are ignored here. I tend to compose the actions and the state separately, eg.:

const clickCounter = Hoverboard({
    add: (state=0, num) => state + num
})

export const model = Hoverboard.compose({
    clickCounter
})

export const actions = {
    addClick: clickCounter.add
}

This works, I guess. But it'd be nice to have the actions automatically available on model, instead of having to pass them along. Maybe something like:

model.clickCounter.add(1)

One simple way to approach this, would be to make the stores (and other functions?) available as properties on the composed store. So that would also allow this:

const currentCount = model.clickCounter.getState()

model.clickCounter.getState(clickCount => console.log('there have been', clickCount, 'clicks'))

And if using Hoverboard.compose to map a store's state, the original store's actions could be passed through as well, eg.

const exaggeratedClicks = Hoverboard.compose(clickCounter, clicks => clicks * 2)

exaggeratedClicks.add(5)

const clicks = exaggeratedClicks() // returns 10

And then, doing nested composition would provide a nested structure of actions, eg.

const model = Hoverboard.compose({
    clicks: Hoverboard.compose({
        clickCounter,
        exaggeratedClicks
    })
})

// passed through to original clickCounter.add() action
model.clicks.exaggeratedClicks.add(3)

model.getState() // returns {"clicks": {"clickCounter": 3, "exaggeratedClicks": 6}}

I think that'd be useful without breaking anything. The actions would map nicely to the structure of the data, by default, and you could still choose to create your own object of action mappings yourself.