Some code inside an effect is reactive and some is not. A chat component that connects to roomId and shows a notification styled with the current theme reads two values, but only one should cause a reconnection. List theme and switching to dark mode drops the connection; leave it out and the notification uses whatever theme was current when the effect last ran.
useEffectEvent, added in React 19.2 7,897 , marks the non-reactive part. The function it returns always sees the latest props and state, yet never counts as a dependency.
import { useEffect, useEffectEvent } from 'react';
function ChatRoom({ roomId, theme }) {
const onConnected = useEffectEvent(() => {
showNotification('Connected!', theme); // always the latest theme
});
useEffect(() => {
const conn = createConnection(roomId);
conn.on('connected', () => onConnected());
conn.connect();
return () => conn.disconnect();
}, [roomId]); // theme deliberately absent
return <Messages roomId={roomId} />;
}The rules are narrow on purpose, because an effect event is a hole in the reactive model:
Declare it at the top level of a component or custom Hook, never in a loop or condition.
Call it only from effects in the same component, never during render or from an event handler.
Never pass it to another component or Hook, and never list it as a dependency.
Read the split out loud: "connect to the room, and when that happens, notify with the current theme." Before the comma is the effect; after it is the event.