useEffect Is Not a Lifecycle Method
Treating useEffect like componentDidMount causes subtle race conditions. The real mental model is synchronization with external state, not timing.
This piece challenges the common habit of mapping useEffect onto class-component lifecycle methods like componentDidMount and componentDidUpdate. That mental model feels intuitive at first but quietly breaks down, especially in data-fetching scenarios where it produces hard-to-debug race conditions.
The author reframes useEffect around synchronization rather than timing: the hook's job is to keep something outside React in sync with current state, not to fire at a particular point in a component's life. A classic fetch-by-userId example illustrates the failure mode—when the dependency changes without cancelling the prior request, a slower response can overwrite fresher data. The fix is a cancellation flag in the cleanup function, which isn't about unmounting at all; it's about tearing down the previous sync cycle before the next one begins.
The practical takeaway for engineers is a shift in the question they ask: instead of 'when does this effect run,' think 'what am I synchronizing, and with what.' That framing makes cleanup logic in subscriptions and fetches far more predictable and helps avoid a class of subtle, easily overlooked bugs in React codebases.