Reactive iteration and conditionals for
@preact/signals
.
By default, @preact/signals
assumes you will use standard Virtual DOM rendering for conditions and loops, and does not provide a reactive primitive for these constructs.
Fortunately, Signal.prototype
is just an import away. We can easily add our own reactive primitive and hang it off of Signal, or provide it as a Component. We can also provide implementation's of Solid's <For each={signal}>{item => ...}</For>
and <Show when={signal}>...</Show>
using the same technique.
import { render } from 'preact';
import { useSignal, signal } from '@preact/signals';
import { For } from './signal-map.js';
const item = (text, done = false) => ({ text: signal(text), done: signal(done) });
const items = signal([item('a'), item('b'), item('c')]);
function App() {
const text = useSignal('');
function add(e) {
items.value = items.value.concat(item(text.value));
text.value = '';
e.preventDefault();
}
return (
<form onSubmit={add}>
<input onInput={e => text.value = e.target.value} />
<ul>
<For each={items}>
{item => (
<li style={item.done.value ? 'text-decoration:line-through':''}>
<input type="checkbox" checked={item.done.value} onInput={() => item.done.value = !item.done.value} />
{' '}
{item.text}
</li>
)}
</For>
</ul>
</form>
);
}
Longer example, this time using Signal.prototype.map()
:
let update = 0;
function Counter() {
const letters = useSignal("ABDEFGHIJKLMNOP");
const position = useSignal(0);
const chars = useComputed(() => ([...letters.value]));
return <>
<input type="text" value={letters} onInput={e => letters.value = e.currentTarget.value}/>
<input type="number" value={position} onInput={e => position.value = +e.currentTarget.value}/>
<ul>
{chars.map((char, index) => (
// you can use hooks in the map() callback, since it's actually a component:
<li>{char} ({update++}) {useComputed(() => index === position.value ? "active" : null)}</li>
))}
</ul>
</>
}
This was very useful. Should the
let value = each.value
maybe check if each is a signal and otherwise just accept static values? I often find myself wanting to pass a normal (often static) list to For.