@gjmcn/happy
v0.3.0
Published
A few helper/wrapper functions to simplify Hyperapp code.
Downloads
5
Readme
Happy
A few helper/wrapper functions to simplify Hyperapp code.
Install
npm install --save @gjmcn/happy
Hyperapp is included in Happy.
Import
E.g. from CDN:
import {h, u, ur, app, memo} from 'https://cdn.skypack.dev/@gjmcn/happy';
Example
The To Do app from the Hyperapp README using Happy functions:
<script type="module">
import {u, app, main, h1, input, ul, li, button} from "https://cdn.skypack.dev/@gjmcn/happy";
const AddTodo = u('todos', s => {
s.todos.push(s.value);
s.value = '';
});
const NewValue = u((s, event) => {
s.value = event.target.value;
});
app({
init: {todos: [], value: ''},
view: s => main(
h1('To do list'),
input({type: 'text', oninput: NewValue, value: s.value}),
ul(s.todos.map(todo => li(todo))),
button({onclick: AddTodo}, 'New!'),
)
})
</script>
Usage
Elements
The virtual node function h
has parameters tag
, props
, child_0
, child_1
, child_2
, ... However:
If
props
is a string, array, or virtual node, it is actually used aschild_0
and an empty object is used forprops
.child_
arguments that are arrays are flattened (nesting is removed) and spread into separate arguments.Only
tag
is required.
Happy also includes element functions for most HTML and SVG elements (see index.js
for the full list). These functions are like h
without the tag
parameter:
p('Some text');
p({class: 'some-class'}, 'Some text');
input({type: 'range'});
Import element functions along with other Happy functions. E.g.
import {u, app, div, p, span} from 'happy';
Text
h
automatically applies Hyperapp's text
function to child elements that are strings, so the text
function is not used explicitly:
// hyperapp
h('div', {}, text('Hello'));
// happy
div('Hello');
Updates
The u
function creates an update: an action where we mutate the state directly. u
is passed a function f
which has state
and (optionally) payload
parameters. u
returns a new function which also has state
and payload
parameters. The new function shallow copies the state, calls f
with the copied state and payload, and returns the copied state.
For brevity, we call the state parameter s
rather than state
, and typically use s
explicitly when accessing state properties (rather than destructuring).
// hyperapp (increase count property by 1)
const Increment = state => ({...state, count: state.count + 1});
// happy
const Increment = u(s => s.count++);
// hyperapp (set user property to name)
const SetUser = (state, name) => ({...state, user: name});
// happy
const SetUser = u((s, name) => s.user = name);
When updating one or more properties that are themselves objects or arrays, we can tell u
to shallow copy these properties (as well as the state) by passing the property names as arguments. The function is passed after the property names:
// hyperapp (toggle element of highlight property)
const ToggleHighlight = (state, index) => {
const highlight = [...state.highlight];
highlight[index] = !highlight[index];
return {...state, highlight};
};
// happy
const ToggleHighlight = u('highlight', (s, index) => {
s.highlight[index] = !s.highlight[index];
})
The ur
function is the same as u
except that the new function returns whatever f
(the passed function) returns. Use ur
for an action that modifies the state and returns [ModifiedState, ...Effects]
.
Notes:
When using
u
, no special action is required when the state is an array — unlike with standard actions. (There is no reason to useur
to only return the modified state, but if this is done and the state is an array, the state must be wrapped in another array.)u
andur
only shallow copy the state and the specified top-level properties. If any other properties are to be mutated, manually shallow copy them insidef
.u
andur
are just convenience functions for creating actions. Standard actions can be used as normal.
The app
Function
app
is used as in Hyperapp except that:
The
node
property can be a DOM element (as in Hyperapp) or a CSS query string — the app is inserted into the first corresponding element. If the node property is absent or falsy, the app is appended to the end of the document body.The
init
option is not required: ifinit
is omitted,null
orundefined
, the initial state is an empty object. (As normal with Hyperapp, if the initial state is an array, it must be wrapped in another array, e.g.[[4, 5, 6]]
or[[4, 5, 6], someEffect]
.)