Custom Helper
To create a custom helper, all you need to do is create a
function. If you expect to receive a state value you can wrap it
in the
helper
function and handle the state value. All
that this library can help you with.
Let's look at this simple render of a todo list.
const [todos, updateTodos] = state([
{name: "buy groceries", status: "pending"},
{name: "go to gym", status: "done"},
{name: "prepare for xmas", status: "pending"},
{name: "start a yt channel", status: "pending"},
])
html`${repeat(todos, t => html`<p>${t.name}</p>`)}`
Let's build a helper that can filter the todos based on status.
The simplest way is to just create a function that takes a list and function that returns a boolean and gets called for each item.
const filterList = (list, filterer) => {
return list.filter(filterer)
}
We can use such a filter function as so:
html`${repeat(
filterList(
todos(),
t => t.status === "done"
),
t => html`<p>${t.name}</p>`
)}`
This works perfectly for the first render. It fails when the todo list state changes. This is because the template only performs a DOM update when it sees the state.
The first thing we can do is wrap the function with the helper function so the template knows to look for a state.
const filterList = helper((list, filterer) => {
return list.filter(filterer)
})
But we are not passing the todos
state to the
helper, we are just calling it to pass the value to the
filterList
.
html`${repeat(
filterList(
todos(), // <- here
t => t.status === "done"
),
t => html`<p>${t.name}</p>`
)}`
Instead, we can call it inside the helper instead.
const filterList = helper((list, filterer) => {
return list().filter(filterer)
})
All the issues are now fixed and the final result looks like so.
const filterList = helper((list, filterer) => {
return list().filter(filterer)
});
html`${repeat(
filterList(
todos,
t => t.status === "done"
),
t => html`<p>${t.name}</p>`
)}`
However, the filterList
only works for state
values. Ideally, you want a helper to work if a state, raw, or
helper value is provided. To do that, we can use the
val
utility.
const filterList = helper((list, filterer) => {
return val(list).filter(filterer)
})
Now the helper is complete and would work for any type of data. Here is the full example in typescript.
import {
html,
state,
helper,
val,
repeat,
StateGetter
} from "@beforesemicolon/markup";
const filterList = helper(<T>(
list: T[] | StateGetter<T[]>,
filterer: (item: T) => boolean
) => {
return val<T[]>(list).filter(filterer)
});
interface ToDo {
name: string;
status: "done" | "pending";
}
const [todos, updateTodos] = state<ToDo[]>([
{name: "buy groceries", status: "pending"},
{name: "go to gym", status: "done"},
{name: "prepare for xmas", status: "pending"},
{name: "start a yt channel", status: "pending"},
])
html`${repeat(
filterList(
todos,
t => t.status === "done"
),
t => html`<p>${t.name}</p>`
)}`.render(document.body);