useEffectEvent: Solving Stale Closures Forever
Stop hacking refs—use the stable hook designed for non-reactive logic
The "stale closure" is the most common bug in React applications. You have a useEffect that needs to read a value (like a prop or state), but you don't want the effect to re-run when that value changes. So you omit it from the dependency array, and suddenly your effect is reading old data.
The Old Hack: useRef
For years, we solved this with the useRef pattern:
// The "latest ref" pattern
const onScrollRef = useRef(onScroll);
onScrollRef.current = onScroll;
useEffect(() => {
const handleScroll = () => {
// Read the latest value from the ref
onScrollRef.current();
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []); // Safe because refs are stableIt works, but it's verbose, ugly, and feels like a hack.
The Solution: useEffectEvent
React 19.2 introduces useEffectEvent (formerly useEvent). It creates a stable function handle that always has access to the latest props and state, but doesn't trigger re-renders.
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
// 1. Create a stable event handler
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme); // Reads latest 'theme'
});
useEffect(() => {
const connection = createConnection(roomId);
connection.on('connected', () => {
onConnected(); // Call the stable handler
});
connection.connect();
return () => connection.disconnect();
}, [roomId]); // 'theme' is NOT a dependency!
}Reactive vs. Non-Reactive
This hook formalizes the separation between:
- Reactive Code (useEffect): Code that should run when things change (e.g., connecting to a new room when
roomIdchanges). - Non-Reactive Code (useEffectEvent): Code that should run in response to an event, reading the latest values without re-triggering the setup (e.g., showing a notification with the current theme).
It's the missing piece of the React reactivity puzzle.
Advertisement
Explore these curated resources to deepen your understanding
Official Documentation
Related Insights
Explore related edge cases and patterns
Advertisement