Which Effect Fetches Data Once on Mount?
Which option loads the users once, when Users first renders? Explain what goes wrong with the others.
// A
useEffect(() => {
fetch("/api/users").then((res) => res.json()).then(setUsers);
});
// B
useEffect(async () => {
const res = await fetch("/api/users");
setUsers(await res.json());
}, []);
// C
useEffect(() => {
fetch("/api/users").then((res) => res.json()).then(setUsers);
}, []);
// D: no effect at all, in the component body
fetch("/api/users").then((res) => res.json()).then(setUsers);Hints
Hint 1
An effect with no dependency array runs after every render.
Hint 2
An async function returns a promise, and React expects an effect to return a cleanup function or nothing.
Solution
Show the approach
Answer: C
- A has no dependency array, so it runs after every render. The state update causes a render, which runs the effect again: a loop.
- B passes an
asyncfunction directly. It returns a promise where React expects a cleanup function. Define the async function inside the effect and call it. - D fetches in the render body, so every render starts another request.
- In real code, C also ignores a response that arrives after unmount: set a flag, or abort with an
AbortController, in the effect's cleanup.
Worth saying out loud
- In development, React's Strict Mode mounts effects twice on purpose — a correct cleanup makes that harmless.
- Data libraries (React Query, SWR) or a framework's loader handle caching, deduplication and races that a bare effect doesn't.