Andrew's Digital Garden

useEffectEvent - separating effects from reactive values

This is an experimental API, but helps with one of the big problems with [[20220815044638-useeffect]], separating reactive and non-reactive logic.

Props, state, and variables declared inside your component’s body are called reactive values. Reactive values can change due to a re-render. Logic inside effects is reactive. Logic inside event handlers is not reactive. It does not run automatically. Event handlers can read reactive values without “reacting” to their changes.

function ChatRoom({ roomId, theme }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { showNotification('Connected!', theme); }); connection.connect(); // ...

As both roomId and theme are reactive, both should be in the deps array.

function ChatRoom({ roomId, theme }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { showNotification('Connected!', theme); }); connection.connect(); return () => { connection.disconnect() }; }, [roomId, theme]); // ✅ All dependencies declared // ...

However, You don't want this to execute based on the theme changing, only if the roomId does. The above code has an issue - if the theme changes, it will re-connect to the room.

You need a way to separate this non-reactive logic from the reactive Effect around it.

React has some suggestions on how to solve this problem (https://react.dev/learn/removing-effect-dependencies#removing-unnecessary-dependencies). Which are loosely:

This is where useEffectEvent comes in. It's currently still experimental, but in the above example, it would allow you to separate the non-reactive logic out:

function ChatRoom({ roomId, theme }) { const onConnected = useEffectEvent(() => { showNotification('Connected!', theme); }); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { onConnected(); }); connection.connect(); return () => connection.disconnect(); }, [roomId]); // ✅ All dependencies declared // ...

React docs mentions the following:

After useEffectEvent becomes a stable part of React, we recommend never suppressing the linter.

https://react.dev/learn/separating-events-from-effects

[[react]]

useEffectEvent - separating effects from reactive values