useEffect in React 19: What's Changed
useEffect remains one of the most misunderstood React APIs in 2026, but the mental model has become clearer with React's official guidance and the patterns established by the ecosystem. React 19's compiler (React Forget, now stable) automatically manages many cases where developers previously needed to manually write useEffect with careful dependency arrays. Understanding when useEffect is still needed — and when it's an anti-pattern — is the key to writing correct React components.
The Dependency Array: The Source of Most Bugs
Every value used inside useEffect that can change over the component's lifetime must be in the dependency array. The React ESLint plugin (react-hooks/exhaustive-deps) enforces this rule and should be enabled in every React project. Violating the rule produces stale closure bugs — the effect reads an outdated value from a previous render. Never disable the exhaustive-deps rule for individual lines without understanding why it's warning; fix the underlying issue instead.
Common dependency array mistakes: including objects or arrays that are recreated on every render (causing infinite loops — use useMemo to stabilize them), omitting functions defined in the component body (use useCallback to stabilize them or define them inside the effect), and including values that should be read at event time not render time (use refs instead of state for values that shouldn't trigger re-renders).
The Cleanup Function
Effects that set up subscriptions, timers, or event listeners must return a cleanup function that tears them down. In React Strict Mode (development), effects run twice — setup, cleanup, setup — to surface bugs in effects that don't clean up correctly. If your effect breaks in Strict Mode, the cleanup function is wrong or missing. Common cleanup patterns: clearTimeout/clearInterval for timers, controller.abort() for fetch requests using AbortController, and unsubscribe() for event emitters and store subscriptions.
When NOT to Use useEffect
The React team's "You Might Not Need an Effect" documentation lists common anti-patterns: transforming data for rendering (use derived state or useMemo instead), handling user events (use event handlers instead), resetting state when props change (use the key prop instead), fetching data (use React Query, SWR, or the new use() hook with Suspense instead). In React 19, the use() hook with Suspense-compatible data sources is the recommended pattern for data fetching, eliminating the need for useEffect-based data fetching in most cases. Download React hooks pattern library at proofmatcher.com.
Originally published at https://proofmatcher.com/blogs/react-useeffect-guide-2026
Top comments (0)