ToDo List with Signals
Port of the original Preact/signals ToDo example Playground
memo
logic added by @cucumba :
TSX
import { h, render } from "preact"
import { memo } from "preact/compat"
import { signal, computed } from "preact/signals"
const todos = signal([
{ text: "Write my first post", completed: true },
{ text: "Buy new groceries", completed: false },
{ text: "Walk the dog", completed: false },
]);
const completedCount = computed(() => {
return todos.value.filter(todo => todo.completed).length
});
const newItem = signal("")
function addTodo() {
todos.value = [...todos.value, { text: newItem.value, completed: false }]
newItem.value = "" // Reset input value on add
}
function removeTodo(index) {
todos.value.splice(index, 1)
todos.value = [...todos.value]
}
function areEqual(prevProps, nextProps) {
if (prevProps.todo.completed !== nextProps.todo.completed) {
return false
}
if (prevProps.todo.text !== nextProps.todo.text) {
return false
}
if (prevProps.index !== nextProps.index) {
return false
}
return true
}
const TodoItem = memo(({ todo, index }) => {
// log(todo.text); // should only be called for new/changed elements
const onCheckboxChange = (event) => {
const newTodo = { ...todo, completed: event.target.value }
todos.value = [
...todos.value.slice(0, index),
newTodo,
...todos.value.slice(index + 1),
]
}
return (
<div class="flex-row items-center mb-2">
<toggle onValueChanged={onCheckboxChange} value={todo.completed} />
{todo.completed ? <s>{todo.text}</s> : todo.text}{" "}
<button onClick={() => removeTodo(index)} text="X" />
</div>
)
}, areEqual)
function TodoList() {
const onInput = event => (newItem.value = event.target.value);
const elements = todos.value.map((todo, index) => (
<TodoItem key={index} todo={todo} index={index} />
))
return (
<div class="p-2">
<div class="flex-row">
<textfield class="grow" text={newItem.value} onInput={onInput} />
<button class="shrink" onClick={addTodo} text="Add" />
</div>
<div class="p-2">
<ul>{elements}</ul>
<p>{`Completed count: ${completedCount.value}`}</p>
</div>
</div>
)
}
render(<TodoList />, document.body)