Write a list with useOptimistic that adds items at once and rolls back when saving fails
Write TeaList, the default export. It takes initialTeas, an array of { id, name }, and saveTea(name), which returns a promise of the saved tea, { id, name }, or rejects when saving fails.
Show the teas in a list, one list item each, and below it a form with a text box labelled Tea (its name is name) and an Add button. Submit the form with a form action. Trim the name, and add nothing when it's empty.
A new tea shows at the end of the list at once, as Sencha (saving…) (with the single … character), while saveTea runs. When it resolves, the saved tea takes its place. When it rejects, the tea disappears and an element with role="alert" says Couldn't add Sencha.; the next tea that saves clears that message. Several teas can be saving at once, and each one must show.
<TeaList initialTeas={[{ id: 1, name: "Assam" }]} saveTea={saveTea} />
// Assam
// Sencha (saving…)
// [Tea: ______] [Add]
import { startTransition, useOptimistic, useState } from "react";
export default function TeaList({ initialTeas = [], saveTea }) {
const [teas, setTeas] = useState(initialTeas);
async function addTea(formData) {
const name = String(formData.get("name") ?? "").trim();
if (!name) return;
// TODO: show the tea at once, marked (saving…), and roll it back if saveTea fails.
const saved = await saveTea(name);
startTransition(() => setTeas((current) => [...current, saved]));
}
return (
<>
<ul>
{teas.map((tea) => (
<li key={tea.id}>{tea.name}</li>
))}
</ul>
<form action={addTea}>
<label>
Tea <input name="name" />
</label>
<button>Add</button>
</form>
</>
);
}
- shows the teas it starts with
- shows a new tea at once, marked (saving…)
- swaps in the saved tea when saveTea answers
- rolls back and explains when saveTea fails
- shows every tea that is saving at once
- a saved tea never shows next to its pending copy
- adds nothing for an empty name
- a tea that saves clears the old message
The answer
Write it yourself first. Then compare it with a reference solution and the checks it passes.
Checking your sign-in…