Lesson 9 — Effects, cancellation, and event reasoning¶
Objective¶
Synchronize with an external request safely and remove effects that only derive render data.
Prerequisites¶
Lesson 8 — Fetching and async state. You should be able to model async state with a repository boundary.
Mental Model¶
An effect synchronizes committed UI with a system outside React. Setup and cleanup form one process. Development remounting tests whether that process is reversible.
Concept¶
Effects are for synchronization with external systems, not for deriving state. A useEffect with AbortController lets you cancel an in-flight request if the component unmounts or dependencies change. Handlers, not effects, are the right place for user-triggered actions.
Example¶
useEffect(() => {
const controller = new AbortController();
fetchTopicDetails(topicId, { signal: controller.signal })
.then((details) => {
if (!controller.signal.aborted) {
setDetails({ status: 'success', data: details });
}
})
.catch((error) => {
if (!controller.signal.aborted) {
setDetails({ status: 'error', error });
}
});
return () => controller.abort();
}, [topicId]);
Real-World Usage¶
Subscribing to WebSocket events, focusing an element after mount, or triggering a fetch when a route parameter changes.
Common Mistakes¶
- Fetching inside an effect when an event handler is more appropriate.
- Ignoring the dependency list and creating stale closures.
- Forgetting cleanup, which leads to state updates on unmounted components.
Mini Lab¶
Task¶
Debounce remote suggestions or fetch topic details. Use AbortController in cleanup, guard result relevance, and move pure filtering back into render.
Constraints¶
- Type quickly, navigate away mid-request, trigger an error, and run under
StrictMode. - Confirm obsolete work cannot update visible state.
Expected Result¶
Rapid input does not fire a request for every keystroke. Navigating away cancels pending work, and StrictMode double-mount does not leave dangling network calls.
Hints¶
Prefer event handlers for user-triggered actions and render for calculations. Use effects only where an external lifecycle must follow committed values.
Knowledge Check¶
- Effect versus event handler?
- What does the dependency list mean?
- Why is ignoring exhaustive dependencies risky?
Challenge¶
A teammate proposes moving all fetch calls into effects with useEffect and no event handlers. What would break, and what code shape would you recommend instead?
Summary¶
Use effects to synchronize with external systems. Cleanup must be reversible, and handlers are usually better than effects for user actions.