fix: stop Dictation panel crashing with React #185 (infinite render loop) #4743
ccantynzcommented Jun 13, 2026
Originally written by @vercel[bot] on GitHub.
The latest updates on your projects. Learn more about Vercel for GitHub.
| Project | Deployment | Actions | Updated (UTC) |
|---|---|---|---|
| voxlen | Preview, Comment | Jun 13, 2026 4:57am |
Cross-repo impact
See what breaks downstream if this PR merges.
⮌ Merged
This pull request was merged into main.
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts
Originally written by @ccantynz-alt on GitHub.
Imported from https://github.com/ccantynz-alt/voxlen/pull/58
Problem
The Dictation view crashed on mount with:
The Dictation
ErrorBoundarycaught it and showed "An unexpected error occurred in Dictation."Root cause
DictationPanelselected its client list directly with a.filter()inside the Zustand selector:const allClients = useClientsStore((s) => s.clients.filter((c) => !c.archived));Zustand v5 is backed by React's
useSyncExternalStore, which calls the selector during render and again immediately afterwards to detect store changes..filter()allocates a brand-new array on every call, so the two snapshots were never referentially equal ([] !== []). React concluded the store never settles, force-re-rendered, and looped until it threw "Maximum update depth exceeded" (React #185).This fired on every mount of the Dictation panel, regardless of how many clients existed, because even an empty
.filter()returns a fresh array.Fix
Wrap the selector in
useShallow, which memoises the result with a shallow compare so the reference stays stable across renders:const allClients = useClientsStore(useShallow((s) => s.clients.filter((c) => !c.archived)));This is the idiomatic Zustand v5 pattern for selectors that derive a new array/object. A full sweep confirmed this was the only new-reference-returning selector in the codebase (the
.reduce()selectors inClientsPanelreturn scalars;ClientsPanelderives its filtered lists in the component body).Tests
Added
DictationPanel.test.tsx:Generated by Claude Code