fix(windows): wrap pinnedCards Zustand selector in useShallow#139
Open
Aryansharma28 wants to merge 1 commit into
Open
fix(windows): wrap pinnedCards Zustand selector in useShallow#139Aryansharma28 wants to merge 1 commit into
Aryansharma28 wants to merge 1 commit into
Conversation
Symptom: blank window on app start — board renders nothing, devtools shows "Maximum update depth exceeded" originating from BoardView, with the preceding warning "The result of getSnapshot should be cached to avoid an infinite loop". Cause: `useBoardStore((s) => s.pinnedCards())` calls the computed helper on every selector run, which returns a *new* filtered+sorted array each time. Zustand v5 wraps React's useSyncExternalStore and treats a new array reference as a state change → React re-renders → selector runs again → new array → infinite loop → React unmounts the whole tree. Latent bug — has been broken since Zustand bumped to ^5.0.4. Likely masked until a particular state shape exposed the loop reliably. Fix: `useBoardStore(useShallow((s) => s.pinnedCards()))`. useShallow does a shallow array comparison, so a new array reference doesn't trigger a re-render when its contents (card refs) are unchanged. This is the canonical Zustand v5 pattern for selectors returning derived collections. Verified: - `npm run build` green - App window now renders the board instead of staying blank Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
App starts but the window is blank. DevTools shows:
```
Warning: The result of getSnapshot should be cached to avoid an infinite loop
at BoardView (BoardView.tsx:43:119)
→ Error: Maximum update depth exceeded.
```
React unmounts the entire tree → nothing renders.
Cause
```ts
// BoardView.tsx:24 — before
const pinnedCards = useBoardStore((s) => s.pinnedCards());
```
`pinnedCards()` is a computed helper that returns a freshly `.filter().sort()`-ed array on every call. Zustand v5 wraps React's `useSyncExternalStore`, which treats a new array reference as a state change → re-render → selector runs again → new array → infinite loop.
Latent since the Zustand v5 bump (`^5.0.4`). Surfaced consistently in current main; probably required a particular pinned-card state shape to trigger reliably before.
Fix
```ts
// after
import { useShallow } from "zustand/react/shallow";
const pinnedCards = useBoardStore(useShallow((s) => s.pinnedCards()));
```
`useShallow` does a shallow-equality compare on the returned array, so a new reference with the same card elements doesn't trigger a re-render. Canonical Zustand v5 pattern for selectors returning derived collections.
Test plan
🤖 Generated with Claude Code