obsidian/wiki/concepts/websocket-react-token-guard.md
2026-04-30 21:18:40 +01:00

57 lines
2.1 KiB
Markdown

---
title: "WebSocket React Token Guard"
tags: [react, websocket, auth, hooks, javascript]
sources: [99 Daily/2026-04-29.md]
created: 2026-04-30
updated: 2026-04-30
---
# WebSocket React Token Guard
A React `useEffect` that opens a WebSocket connection must guard against a null/undefined auth token. Without the guard, the socket opens immediately (before auth resolves), the server rejects the handshake, and the socket closes — leaving the UI in a broken state with no obvious error.
## The Bug
```tsx
useEffect(() => {
const ws = new WebSocket(`wss://api.example.com/ws?token=${token}`);
// token is null on first render — ws opens with "token=null", server closes it
ws.onmessage = (e) => setData(JSON.parse(e.data));
return () => ws.close();
}, []); // ← missing token in dep array; never reconnects after login
```
Two mistakes:
1. No early return when `token` is `null` or `undefined`
2. `token` not in the dependency array — even if auth eventually resolves, the effect never re-runs
## Fixed Pattern
```tsx
useEffect(() => {
if (!token) return; // ← guard: do nothing until auth is ready
const ws = new WebSocket(`wss://api.example.com/ws?token=${token}`);
ws.onmessage = (e) => setData(JSON.parse(e.data));
return () => ws.close(); // cleanup closes previous socket on token change
}, [token]); // ← re-runs when token appears or changes
```
## Why Both Changes Are Required
| Fix | Without it |
|-----|-----------|
| `if (!token) return` | Socket opens with `token=null` → server rejects → immediate close |
| `token` in dep array | Socket never reconnects after login because effect only ran once (with null token) |
## Lifecycle on First Render
1. Component mounts, `token` is `null` → effect runs → `if (!token) return` exits early
2. Auth resolves, `token` is set → React re-runs effect because `token` is a dep
3. Effect runs again with a valid token → socket opens and stays open
## Related
- [[wiki/concepts/time-sleep-blocks-asyncio|time-sleep-blocks-asyncio]] — another async lifecycle pitfall
- [[wiki/tech-patterns/react-patterns|react-patterns]] — React hook conventions