State & lists
useState with an updater function, useMemo for a derived count, a controlled input, and a keyed list.
import { useState, useMemo } from "react";
const TASKS = [
{ id: 1, title: "Learn hooks", done: true },
{ id: 2, title: "Build a list", done: false },
{ id: 3, title: "Ship it", done: false },
];
export default function App() {
const [tasks, setTasks] = useState(TASKS);
const [draft, setDraft] = useState("");
const remaining = useMemo(() => tasks.filter((task) => !task.done).length, [tasks]);
function toggle(id) {
setTasks((current) =>
current.map((task) => (task.id === id ? { ...task, done: !task.done } : task))
);
}
function add(event) {
event.preventDefault();
if (!draft.trim()) return;
setTasks((current) => [...current, { id: Date.now(), title: draft.trim(), done: false }]);
setDraft("");
}
return (
<main className="app">
<h1>Tasks</h1>
<p className="count">{remaining} left</p>
<form onSubmit={add}>
<input
value={draft}
onChange={(event) => setDraft(event.target.value)}
placeholder="Add a task…"
/>
<button type="submit">Add</button>
</form>
<ul>
{tasks.map((task) => (
<li key={task.id} className={task.done ? "done" : ""}>
<label>
<input type="checkbox" checked={task.done} onChange={() => toggle(task.id)} />
{task.title}
</label>
</li>
))}
</ul>
</main>
);
}