How do you fetch data with useEffect and prevent memory leaks?

This tests useEffect async lifecycle and cleanup. A good answer places fetch inside useEffect, uses an ignore flag or AbortController to block state updates after unmount, and includes deps. A red flag is skipping cleanup and letting a late response set state.
WHAT THIS TESTS: This question evaluates your understanding of the useEffect lifecycle for side effects that involve asynchronous network requests. The interviewer wants to see that you know how to initiate a fetch when a component mounts, protect against updates after unmount, manage the dependency array correctly, and return cleanup logic. It also surfaces whether you understand the difference between the setup function and an async setup function, and how React handles effect cleanup during unmount or re-render.
A GOOD ANSWER COVERS: First, place the fetch call inside the useEffect setup function rather than outside the hook or directly in the component body. Second, declare every reactive value used inside the effect, such as the API URL or query parameters, in the dependency array so the effect re-runs when inputs change. Third, handle the unmount case by returning a cleanup function that either aborts the request with an AbortController or ignores the result with a boolean flag. Fourth, avoid making the setup function itself async because useEffect expects the return value to be either undefined or a cleanup function, not a Promise.
COMMON WRONG ANSWERS: A frequent mistake is writing an async function directly as the useEffect callback, which implicitly returns a Promise and breaks the cleanup contract. Another red flag is omitting the dependency array or leaving out reactive dependencies, which causes stale closures or infinite refetch loops. Simply calling fetch without any cancellation or ignore logic is also weak because it can trigger a memory leak warning or set state on an unmounted component. Finally, putting fetch in the component body without useEffect causes a request on every render.
LIKELY FOLLOW-UPS: The interviewer might ask how you would handle race conditions when the dependency changes quickly, such as a user typing in a search box. They may also ask why you should not use useEffect for data fetching in production React apps, which opens a discussion about server components, React Query, or the fetch-then-render pattern. Another follow-up is how StrictMode intentionally double-invokes effects in development to help you test your cleanup logic.
ONE CONCRETE EXAMPLE: Imagine a UserProfile component that receives a userId prop. You would write useEffect with userId in the dependency array. Inside the setup function, you create an AbortController, pass its signal to fetch, and update state only when the response arrives. In the cleanup function, you call controller.abort. If the component unmounts or userId changes before the fetch finishes, the browser cancels the request and the effect does not attempt to update state.
Source: react.dev
Read the original → react.dev
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.