Modular Hyperapp - Part 7
Tags: hyperapp, javascript
Published: 2020-08-25 20:00:56

> ##### _NOTE: this article series is superceded by [this article](https://dev.to/zaceno/a-fresh-take-on-modular-hyperapp-4njm)_

Welcome to this last part of my series on Modular Hyperapp. I'll summarize what we've gone over so far, then complete the picture before signing off with some final reflections.

### Recap

#### Views

Reusable parts of the view can be broken out as functions called _views_ or _view-components_. Views are provided the values and actions they need through an object I call the _model_ for the view.

#### Primitive Transforms

In the same vein, reusable bits of logic can be broken out from actions in the form  of _primitive transforms_. They take a value, and return a new value – not the full state.

#### Domains

We brought up the concept of "domains" - an area/feature/aspect of your app that makes sense to think about in isolation from the rest. All logic pertaining to a certain domain makes sense to gather in a module.

#### Models

A module could have several views and subscription-components. Since they belong to the same domain, they need roughly the same models. It makes sense to have one common model format for all view- and subscription-components of a module.

#### Wired Actions / Models

In order to move action- and model-definitions to the modules for their respective domains, they need to be defined dynamically in a function I've been calling `wire`.

Actions in a `wire` function know how to operate on a particular value through the `getter` and a `setter`, given as arguments. `wire` return a function which, given the current state, returns the model for the module's views.

#### App Modules

The value that a module's actions operate on is often complex, Therefore modules should also export a value-initializer I've called `init`.

Modules containing an init plus all the actions and views (and subscriptions) a domain needs, I call "app modules" since they can be run as standalone apps.

#### Wiring App Modules to Others

A `wire` may also take _mapped transforms_ as arguments – functions that tell it what to do when "something happens". `wire` can also _return_ mapped transform besides the model-function for passing as arguments to other `wire`s. In this way, modules can be wired together to form a more complicated app.

### Make Modules from Modules

But not just apps! modules could be wired together to form other modules too:

```js
import * as foo from './foo.js'
import * as bar from './bar.js'

export const init = () => ({
    myfoo: foo.init()
    mybar: bar.init()     
}

export const wire = ({getter, setter}) => {
  
    const myfoo = foo.wire({
        getter: state => getter(state).myfoo,
        setter: (state, myfoo) => setter(state, {
            ...getter(state),
            myfoo,
        }),
        onSnap: bar.crackle,
    })

    const mybar = bar.wire({
        getter: state => getter(state).mybar,
        setter: (state, mybar) => setter(state. {
            ...getter(state),
            mybar,
        }),
    })

    return {
        pop: foo.pop,
        model:  state => ({
            myfoo: myfoo.model(state),
            mybar: mybar.model(state),
        })
    }
}

export const view = (model) => h('div', {}, [
    h('p', {}, [ text('Foo:'), foo.view(model.myfoo) ]),
    h('p', {}, [ text('Bar:'), bar.view(model.mybar) ]),
])
```

In this way, an app can be structured as a tree app-modules. Even tiny things that repeat a lot, like a button with some recurring behavior, could be defined once and reused in many places.

### Dynamic Instances

There is just one more thing we need to add to complete the picture: What if there can be multiple instances of some module's values in the state, and we don't know them from the start?

For instance: task-items in a to-do list. – How could we define getters and setters for a task we don't yet know will exist? We  could parameterize the getters and setters, like this:

```js
// this is task-list.js

import * as task from './task.js'

const tasks = task.wire({
    getter: (state, id) => state.tasks[id],
    setter: (state, todo, id) => ({
        ...state,
        tasks: {...state.tasks, [id]: todo}
    }),
})
```

The `id` parameter refers to a specific instance of a task. 
Actions in `task.js` will need to get `id` in the payload, in order to pass it on to `getter` and `setter`.

```js
//this is task.js

export const wire = ({getter, setter}) => {
    
    const SetText = (state, {id, text}) => setter(
        state,
        {...getter(state, id), text},
        id
    )
    //...
```

The only way actions for actions to get the `id` as a payload, is through the model function:

```js
//this is task.js

export const wire = ({getter, setter}) => {
    
    return {
        model: (state, id) => ({
            ...getter(state, id),
            SetText: (_, event) =>
                [SetText, {id, text: event.target.value}],
            //...
        })
        //...
    }
}
//...        
```

The `task-list.js` model can now create a sub-model for each task that happens to exist, each time the state updates:

```js
//this is task-list.js

//...

const model = (state) => ({
    //...
    tasks: Object.keys(state.tasks).map(id =>    
        tasks.model(state, id)
    )
})

//...
```

`id` doesn't have to be a number or a string. It could be a complex object representing a path through a whole tree of dynamic instances. That way you could even have dynamic lists in dynamic lists!

### Finally!

And here we finally are, at the end of the series! Congratulations and well done for sticking it out all the way!

It's been a long and abstract journey to this point, where we finally see that _any app_ – no matter how large or complex – can be made up of self-contained, manageable modules, developed individually and later combined.

### Closing Thoughts

Such structure is similar to what you'd have with React or Vue – which begs the question: Why not just use React or Vue? After all, this app-module pattern I've presented _is_ rather verbose with all its `getter`s, `setter`s, `state`s and `id`s.

I wouldn't presume to tell you the right framework to use in your particular situation, but allow me to make a case for Hyperapp:

First, I wrote this series to show how far you _could_ take modularization _if you need to_. Hyperapp leaves it up to you to use just the techniques and patterns that help you.

> If you want to dig in to a really over-modular example, I threw everything I had at [this multi-todo-list app](https://github.com/zaceno/multitodo)

Contrast that with more rigid frameworks where everything needs to be a component. You need to decide what each component should do before making it. It seems easy at first, but as you add more components, sharing state between them gets more convoluted – an issue which has led to the development of central-state-stores like Redux and Vuex. All the mechanisms for coordinating components and state come with their own APIs to learn. How much time have you spent pouring over docs and tutorials to figure out React-hooks, redux-saga, et.c? – And making the pieces fit together?

Hyperapp starts from the other end: state is global and shared by default. Nothing is encapsulated until you want to make it that way. The API is minimal and dead simple – it doesn't do everything you'd want, but it also _doesn't get in the way_. You have the full power of javascript at your disposal to structure your app however you please. If you have a bug, odds are it's because you used _javascript_ wrong, not Hyperapp.

The examples here could be made much more concise and readable with some library code. But then I'd be explaining how to use my library code, and not conveying the _ideas_ behind it.

I expect you will figure out patterns and helpers that suit your style. They will probably look different from mine, and that's fine! Regardless of what you get up to, I hope and believe that the ideas I've presented here will help!