Loading, error, ready
Data arrives later — or not at all. A good screen shows *loading*, then the data or a clear error. And when the learner switches dishes fast, an old slow answer must never overwrite a newer one: that's what the effect's cleanup is for.
useDish
function useDish(slug) {
const [state, setState] = useState({ status: "loading" });
useEffect(() => {
let ignore = false;
setState({ status: "loading" });
fetchDish(slug).then(
(dish) => { if (!ignore) setState({ status: "ready", dish }); },
(error) => { if (!ignore) setState({ status: "error", error: error.message }); },
);
return () => {
ignore = true;
};
}, [slug]);
return state;
}
export default function App() {
const [slug, setSlug] = useState("momo");
const result = useDish(slug);
return (
<>
<select id="pick" value={slug} onChange={(e) => setSlug(e.target.value)}>
<option value="momo">Momo</option>
<option value="thukpa">Thukpa</option>
<option value="pizza">Pizza</option>
</select>
{result.status === "loading" && <p id="status">Loading…</p>}
{result.status === "error" && <p id="error" role="alert">{result.error}</p>}
{result.status === "ready" && (
<article>
<h2 id="name">{result.dish.name}</h2>
<p id="price">Rs {result.dish.price}</p>
</article>
)}
</>
);
}