refactor(web): evaluate Zustand for WebSocket and game state management #7

Open
opened 2026-04-17 18:33:06 +00:00 by forgejo-lila · 0 comments
Owner

Background

Currently, WebSocket connection state and multiplayer game state are managed
via React Context scoped to the /multiplayer layout route. This is the right
call for the initial slice — one connection, one game screen, simple state.

As the app grows, context will start showing its limits. This issue tracks
when and whether to introduce Zustand as a state manager.

Triggers — revisit this when any of the following land

1. Reconnection handling slice
Reconnection requires game state (current question, scores, submitted answers,
lobby code) to survive component unmounts during the reconnect cycle. React
state and context are destroyed on unmount. Zustand store persists. This is
probably the strongest trigger.

2. Game state shared across many components
When the game screen grows beyond a single route component into multiple
independent components (ScoreBar, PlayerList, Timer, QuestionCard) each
needing their own slice of game state, prop drilling and context re-renders
become painful. Zustand's per-selector subscriptions solve this cleanly.

3. WebSocket messages updating state outside /multiplayer
If notifications, challenges, or any WS-driven UI needs to appear outside
the multiplayer subtree (e.g. "you've been challenged" toast on the home
page), context scoped to /multiplayer can't reach it. Zustand as a global
store can.

4. Optimistic updates
Chat, reactions, or any fire-and-forget interaction that needs immediate UI
feedback before server confirmation. Zustand handles optimistic update
patterns cleanly.

What to evaluate at that point

  • Replace the WsContext provider with a Zustand store.
  • Move game state (currentQuestion, scores, playerAnswers, roundResults)
    from local component state into Zustand slices.
  • Keep the ws-client.ts abstraction — it doesn't change, only where its
    state lives changes.
  • Evaluate zustand/middleware devtools for debugging game state transitions.

What NOT to do prematurely

  • Don't introduce Zustand just because it's popular.
  • Don't migrate until at least one of the triggers above is real and painful.
  • Don't use Zustand for UI state (loading spinners, form inputs, modal open/closed)
    — that stays local.
  • Reconnection handling slice (future)
  • apps/web/src/lib/ws-client.ts
  • apps/web/src/routes/multiplayer.tsx (layout route + WsProvider)
  • apps/web/src/routes/multiplayer/game.$code.tsx
## Background Currently, WebSocket connection state and multiplayer game state are managed via React Context scoped to the `/multiplayer` layout route. This is the right call for the initial slice — one connection, one game screen, simple state. As the app grows, context will start showing its limits. This issue tracks when and whether to introduce Zustand as a state manager. ## Triggers — revisit this when any of the following land **1. Reconnection handling slice** Reconnection requires game state (current question, scores, submitted answers, lobby code) to survive component unmounts during the reconnect cycle. React state and context are destroyed on unmount. Zustand store persists. This is probably the strongest trigger. **2. Game state shared across many components** When the game screen grows beyond a single route component into multiple independent components (ScoreBar, PlayerList, Timer, QuestionCard) each needing their own slice of game state, prop drilling and context re-renders become painful. Zustand's per-selector subscriptions solve this cleanly. **3. WebSocket messages updating state outside `/multiplayer`** If notifications, challenges, or any WS-driven UI needs to appear outside the multiplayer subtree (e.g. "you've been challenged" toast on the home page), context scoped to `/multiplayer` can't reach it. Zustand as a global store can. **4. Optimistic updates** Chat, reactions, or any fire-and-forget interaction that needs immediate UI feedback before server confirmation. Zustand handles optimistic update patterns cleanly. ## What to evaluate at that point - Replace the `WsContext` provider with a Zustand store. - Move game state (currentQuestion, scores, playerAnswers, roundResults) from local component state into Zustand slices. - Keep the `ws-client.ts` abstraction — it doesn't change, only where its state lives changes. - Evaluate `zustand/middleware` devtools for debugging game state transitions. ## What NOT to do prematurely - Don't introduce Zustand just because it's popular. - Don't migrate until at least one of the triggers above is real and painful. - Don't use Zustand for UI state (loading spinners, form inputs, modal open/closed) — that stays local. ## Related - Reconnection handling slice (future) - `apps/web/src/lib/ws-client.ts` - `apps/web/src/routes/multiplayer.tsx` (layout route + WsProvider) - `apps/web/src/routes/multiplayer/game.$code.tsx`
forgejo-lila added this to the lila development project 2026-04-17 18:33:06 +00:00
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: forgejo-lila/lila#7
No description provided.