Hooks and effects
Why Effects run twice in development
Treat development remounting as a test that exposes missing cleanup instead of adding a flag to hide the second call.
Strict Mode adds development checks that expose unsafe rendering and missing Effect cleanup. You will see this as soon as you add logging inside useEffect.
For an Effect, React may run this sequence in development:
- Start synchronization.
- Clean it up.
- Start it again.
This simulates leaving and returning to the screen. A correct Effect remains safe because cleanup fully stops the first synchronization. If you see duplicate network requests or duplicate subscriptions in dev, missing cleanup is the first suspect.
Do not hide the second run with a ref flag:
if (hasRun.current) return
hasRun.current = true
That can hide the warning while leaving the real remount bug. If the user navigates away and back, the component still needs to reconnect correctly. The flag tricks you into thinking the Effect is fine when it is not.
Fix the pair instead:
useEffect(() => {
const connection = createConnection(roomId)
connection.connect()
return () => connection.disconnect()
}, [roomId])
Every connect() has a matching disconnect(). When roomId changes, the old connection closes before the new one opens.
Operations such as purchases and form submissions belong in event handlers, where a specific action caused them. Server endpoints for important operations should also handle retries and duplicates safely.
Strict Mode behavior is development-only, but the bugs it reveals are real. Production builds do not double-invoke Effects, but users still navigate away and back. Cleanup that works in dev is cleanup that works in prod.
If an Effect fetches data, consider whether a library like SWR or React Query fits better. They handle caching, deduplication, and stale data without you writing all the synchronization by hand.
Log connect and disconnect calls while changing rooms and mounting the component. Every started connection should have a matching cleanup.
Lesson completed