Naive list (hand-rolled fetch)
Each component runs its own network call inside useEffect. Every filter change refetches both lists and you manually juggle loading/error state.
export default function NaiveList({ filter, fetcher }: Props) {
const [items, setItems] = useState<Item[] | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
let cancelled = false;
async function run() {
setLoading(true);
setError(null);
try {
const res = await fetcher("/api/demos/items?filter=" + encodeURIComponent(filter) + "&delay=400&error=0");
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = (await res.json()) as { items: Item[] };
if (!cancelled) setItems(data.items);
} catch (e) {
if (!cancelled) setError((e as Error).message);
} finally {
if (!cancelled) setLoading(false);
}
}
run();
return () => {
cancelled = true;
};
}, [filter, fetcher]);
return (
<div>
{loading ? "Fetching…" : `Loaded ${items?.length ?? 0} items`}
{error && <div>{error}</div>}
{/* render list */}
</div>
);
}