65Debug an infinite useEffect() loop and prevent stale asynchronous responses
Problem
A teammate wrote this profile component. In production it hammers the API with requests, the CPU fan spins up, and when users switch profiles quickly they sometimes see the wrong person.
function UserProfile({ userId, loadUser }) {
const [user, setUser] = useState(null);
const [requests, setRequests] = useState(0);
const params = { id: userId, include: ["posts"] };
useEffect(() => {
setRequests(requests + 1);
loadUser(params).then((data) => setUser(data));
}, [params, requests]);
return (
<section data-requests={requests}>
<h2>{user ? user.name : "Loading…"}</h2>
</section>
);
}loadUser(params) returns a promise that resolves to { id, name }. It also receives a second argument, { signal }, if you want to cancel requests.
Fix UserProfile so that:
- it requests each
userIdexactly once anddata-requestscounts those requests; - after
userIdchanges, it shows "Loading…" until the new user arrives; - it never shows a user whose id differs from the current
userId, whatever order the responses arrive in; - no state is updated after it unmounts.
Keep the same props, markup and data-requests attribute.
Try it yourself
Write your solution, run it, then submit to check it against the tests.
Step-by-step
params is a new object on every render, and React compares dependencies with Object.is. A new object is never equal to the old one, so the effect runs after every render.
Unlock the full solution
What's inside
- Step-by-step
- Approach
- Solution
- Complexity
- Edge cases
- Follow-up questions
- Tests
Includes 2 code listings, 4 edge cases, 5 follow-up questions.
One-time payment by UPI, card or net banking. Launch price.