1linkimport { List } from 'callbag-jsx';
2link
3linkconst records = state([]);
4linkconst add = () => records.set(records.get().concat(new Date()));
5linkconst clear = () => records.set([]);
6link
7linkrenderer.render(
8link <>
9link <button onclick={add}>Add</button>
10link <button onclick={clear}>Clear</button>
11link <ul>
12link <List of={records} each={record => <li>{record}</li>}/>
13link </ul>
14link </>
15link).on(document.body);
👉 each()
function will be provided with a state object, reflecting
the value of a particular index.
👉 Use .sub()
method to read/track its properties:
1linkconst todos = state([{title: 'Do this'}, {title: 'Do that'}]);
2linkconst next = state('');
3link
4linkconst add = () => {
5link todos.set(todos.get().concat([{title: next.get()}]));
6link next.set('');
7link};
8link
9linkrenderer.render(
10link <>
11link <h1>Todos</h1>
12link <ol>
13link <List of={todos} each={todo => <li>{todo.sub('title')}</li>}/>
14link </ol>
15link <input type='text' _state={next} placeholder='What should be done?'/>
16link <button onclick={add}>Add</button>
17link </>
18link).on(document.body);
👉 Use .get()
method to get a snapshot value.
This can be useful for handling events on items of the list:
1linkconst records = state([]);
2linkconst add = () => records.set(records.get().concat(new Date()));
3linkconst clear = () => records.set([]);
4linkconst remove = date => records.set(records.get().filter(d => d !== date));
5link
6linkrenderer.render(
7link <>
8link <button onclick={add}>Add</button>
9link <button onclick={clear}>Clear</button>
10link
11link <br/>
12link
13link <small>
14link Click on each record to remove it.
15link </small>
16link <ul>
17link <List of={records} each={record =>
18link <li onclick={() => remove(record.get())}>
19link {record}
20link </li>}/>
21link </ul>
22link </>
23link).on(document.body);
The index of each element is passed to each()
as a second argument:
1linkconst records = state([]);
2linkconst add = () => records.set(records.get().concat(new Date()));
3linkconst clear = () => records.set([]);
4linkconst remove = (date) => records.set(records.get().filter(d => d !== date));
5link
6linkrenderer.render(
7link <>
8link <button onclick={add}>Add</button>
9link <button onclick={clear}>Clear</button>
10link
11link <br/>
12link
13link <small>
14link Click on each record to remove it.
15link </small>
16link
17link <List of={records} each={(record, index) =>
18link <div onclick={() => remove(record.get())} class={{odd: index % 2}}>
19link {index + 1} - {record}
20link </div>}
21link />
22link </>
23link).on(document.body);
By default, DOM elements (returned by each()
) are bound to a specific index.
This means, for example, if you prepend an item to your array, contents of all DOM elements will
be updated (since the content of all indices of the array have changed):
In some cases, this ☝️ is not the desired behavior. For example:
You change this behavior by providing a mapping between items of your list and DOM elements, in form of a key function:
1link// map each element to a property
2link//
3linkconst key = x => x.id
4link
5link// or, when list items are unique strings,
6link// use them directly as keys
7link//
8linkconst key = x => x
👉 Pass a key function to key
property of <List/>
for smarter DOM updates:
1linkconst tasks = state([]);
2linkconst next = state('');
3linklet id = 0;
4link
5linkconst add = () => {
6link const newTask = {
7link id: id,
8link title: next.get()
9link };
10link
11link id += 1;
12link next.set('');
13link tasks.set(tasks.get().concat(newTask));
14link}
15link
16linkrenderer.render(
17link <>
18link <input type='text' _state={next}/>
19link <button onclick={add}>Add</button>
20link
21link <br/>
22link
23link <List of={tasks}
24link each={task => <div>{task.sub('title')} ({task.sub('id')})</div>}
25link key={task => task.id}
26link />
27link </>
28link).on(document.body);
info IMPORTANT
The key function passed to MUST return stable and unique strings (or numbers):
- stable means it should return the same value for the same object (so no random stuff).
- unique means it should not return the same value for different objects.
👉 When key
is provided, each()
will be invoked for each unique key (instead of index).
As the index of that key can change, index
parameter
passed to each()
will become a callbag (instead of a fixed number):
1linkconst tasks = state([]);
2linkconst next = state('');
3linklet id = 0;
4link
5linkconst add = () => {
6link const newTask = {
7link id: id,
8link title: next.get()
9link };
10link
11link id += 1;
12link next.set('');
13link tasks.set(tasks.get().concat(newTask));
14link}
15link
16linkconst remove = task => {
17link tasks.set(tasks.get().filter(t => t.id !== task.id));
18link}
19link
20linkrenderer.render(<>
21link <input type='text' _state={next}/>
22link <button onclick={add}>Add</button>
23link
24link <br/>
25link <small>Click on each item to remove it</small>
26link
27link <List of={tasks}
28link each={(task, index) =>
29link <div class={{odd: expr($ => $(index) % 2)}}
30link onclick={() => remove(task.get())}>
31link {expr($ => $(index) + 1)} - {task.sub('title')} ({task.sub('id')})
32link </div>
33link }
34link key={task => task.id}
35link />
36link</>).on(document.body);