02 — Todo list
Arrays, keyed reconciliation, and batched updates. This is where fine-grained reactivity earns its keep — only the changed <li> updates, not the whole list.
tsx
import { use } from 'flexium/core'
type Todo = { id: number; text: string; done: boolean }
let nextId = 1
export function TodoList() {
const [todos, setTodos] = use<Todo[]>([])
const [draft, setDraft] = use('')
function add() {
if (!draft.trim()) return
setTodos(prev => [...prev, { id: nextId++, text: draft, done: false }])
setDraft('')
}
function toggle(id: number) {
setTodos(prev => prev.map(t => t.id === id ? { ...t, done: !t.done } : t))
}
function remove(id: number) {
setTodos(prev => prev.filter(t => t.id !== id))
}
return (
<div>
<input
value={draft}
oninput={(e: any) => setDraft(e.target.value)}
onkeydown={(e: any) => { if (e.key === 'Enter') add() }}
placeholder="What needs doing?"
/>
<button onclick={add}>Add</button>
<ul>
{todos.map(t => (
<li key={t.id} style={{ textDecoration: t.done ? 'line-through' : 'none' }}>
<input type="checkbox" checked={t.done} onchange={() => toggle(t.id)} />
{t.text}
<button onclick={() => remove(t.id)}>×</button>
</li>
))}
</ul>
<p>{todos.filter(t => !t.done).length} remaining</p>
</div>
)
}Why key matters
The key={t.id} attribute lets flexium's renderer (Phase 2's reconcile rewrite) match <li> elements across renders by identity instead of position. When you toggle item 5 in a list of 100, only that one <li> is re-rendered. Without key, the renderer falls back to positional diff and may re-render unrelated nodes.
Batched updates
Multiple setTodos calls in the same synchronous block coalesce into a single flush:
tsx
function checkAll() {
setTodos(prev => prev.map(t => ({ ...t, done: true })))
setSomethingElse(...)
// → 1 microtask flush, 1 DOM patch pass
}Phase 2's DocumentFragment-based batch insert + Range-based batch remove means even a 1,000-item reordering completes in ~4 ms on Chrome.
Try it
- Add a "Clear completed" button using a single
setTodos(prev => prev.filter(t => !t.done)). - Show a
<p>You're all caught up!</p>whentodos.length === 0— flexium will create/destroy that node automatically.
API surface used
use<T[]>([])— typed signal<li key={...}>— keyed list reconciliation
Next
→ Data fetching — use(async fn), Suspense, ErrorBoundary.