57 lines
2.1 KiB
Markdown
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
|