Created
January 18, 2026 19:30
-
-
Save matthew-gerstman/eafdcd797c176547b70f4af57b6550a2 to your computer and use it in GitHub Desktop.
Mirror Convenience Hooks Proposal
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
| # Mirror Convenience Hooks Proposal | |
| ## Overview | |
| This proposal introduces three convenience hooks to reduce unnecessary re-renders and improve developer experience when working with Mirror data. These hooks cover the most common patterns found in the codebase that currently cause suboptimal re-render behavior. | |
| **Current state:** | |
| - `useMirrorList` and `useGlobalMirrorList` now use derived atoms with shallow array equality ✅ | |
| - Selector hooks (PR #3754) were reverted due to complexity concerns ❌ | |
| **Proposed additions:** | |
| | Hook | Purpose | Equality Check | | |
| |------|---------|----------------| | |
| | `useMirrorCount` | Count matching items | `prevCount === nextCount` | | |
| | `useMirrorExists` | Check if any item matches | `prevBool === nextBool` | | |
| | `useMirrorFind` | Find single matching item | `prev?.id === next?.id` | | |
| --- | |
| ## 1. `useMirrorCount<T>` — Count Matching Items | |
| Returns the count of items matching a filter. Only re-renders when the count actually changes. | |
| ### Signature | |
| ```typescript | |
| function useMirrorCount<T extends Resource>( | |
| resourceType: ResourceType, | |
| filterFn: (item: T) => boolean, | |
| memoizeDeps: unknown[] | |
| ): number | |
| ``` | |
| ### Real-World Examples | |
| #### Example 1: Thread Todo Statistics (`use-thread-todos.ts`) | |
| **Current implementation** — re-renders when ANY todo property changes: | |
| ```typescript | |
| export function useThreadTodoStats(threadId: string) { | |
| const todos = useThreadTodos(threadId) | |
| return useMemo(() => { | |
| const total = todos.length | |
| const completed = todos.filter((todo) => todo.status === 'completed').length | |
| const inProgress = todos.filter((todo) => todo.status === 'in_progress').length | |
| const pending = todos.filter((todo) => todo.status === 'pending').length | |
| return { total, completed, inProgress, pending } | |
| }, [todos]) | |
| } | |
| ``` | |
| **With `useMirrorCount`** — only re-renders when counts change: | |
| ```typescript | |
| export function useThreadTodoStats(threadId: string) { | |
| const total = useMirrorCount<ThreadTodo>('thread_todos', | |
| (t) => t.threadId === threadId, [threadId]) | |
| const completed = useMirrorCount<ThreadTodo>('thread_todos', | |
| (t) => t.threadId === threadId && t.status === 'completed', [threadId]) | |
| const inProgress = useMirrorCount<ThreadTodo>('thread_todos', | |
| (t) => t.threadId === threadId && t.status === 'in_progress', [threadId]) | |
| const pending = useMirrorCount<ThreadTodo>('thread_todos', | |
| (t) => t.threadId === threadId && t.status === 'pending', [threadId]) | |
| return { total, completed, inProgress, pending, allCompleted: total > 0 && completed === total } | |
| } | |
| ``` | |
| #### Example 2: Notification Bell Unread Count (`use-notification-bell.ts`) | |
| **Current implementation** — re-renders on any notification change: | |
| ```typescript | |
| export function useNotificationBell() { | |
| const notifications = useMirrorList<Notification>('notifications') | |
| const unreadCount = notifications.filter((n) => !n.readAt).length | |
| const hasUnread = unreadCount > 0 | |
| // ... | |
| } | |
| ``` | |
| **With `useMirrorCount`** — only re-renders when unread count changes: | |
| ```typescript | |
| export function useNotificationBell() { | |
| const unreadCount = useMirrorCount<Notification>('notifications', | |
| (n) => !n.readAt, []) | |
| const hasUnread = unreadCount > 0 | |
| // ... | |
| } | |
| ``` | |
| #### Example 3: Workspace Member Count | |
| **Current implementation** — re-renders when any member changes: | |
| ```typescript | |
| function useWorkspaceMemberCount(workspaceId: string) { | |
| const members = useGlobalMirrorList<WorkspaceMember>('workspace-members', | |
| (m) => m.workspaceId === workspaceId && m.status === 'active', [workspaceId]) | |
| return members.length | |
| } | |
| ``` | |
| **With `useMirrorCount`** — only re-renders when count changes: | |
| ```typescript | |
| function useWorkspaceMemberCount(workspaceId: string) { | |
| return useMirrorCount<WorkspaceMember>('workspace-members', | |
| (m) => m.workspaceId === workspaceId && m.status === 'active', [workspaceId]) | |
| } | |
| ``` | |
| --- | |
| ## 2. `useMirrorExists<T>` — Check If Any Item Matches | |
| Returns `true` if any item matches the filter. Only re-renders when the boolean result changes. | |
| ### Signature | |
| ```typescript | |
| function useMirrorExists<T extends Resource>( | |
| resourceType: ResourceType, | |
| filterFn: (item: T) => boolean, | |
| memoizeDeps: unknown[] | |
| ): boolean | |
| ``` | |
| ### Real-World Examples | |
| #### Example 1: Check If Project Is Favorited (`use-favorites.ts`) | |
| **Current implementation** — re-renders on ANY favorite change: | |
| ```typescript | |
| export function useIsFavorited(resourceType: string, resourceId?: string | null) { | |
| const allFavorites = useGlobalMirrorList<Favorite>('favorites') | |
| return useMemo(() => { | |
| if (!resourceId) return false | |
| return allFavorites.some( | |
| (f) => f.resourceType === resourceType && f.resourceId === resourceId && !f.deletedAt | |
| ) | |
| }, [allFavorites, resourceType, resourceId]) | |
| } | |
| ``` | |
| **With `useMirrorExists`** — only re-renders when favorited status changes: | |
| ```typescript | |
| export function useIsFavorited(resourceType: string, resourceId?: string | null) { | |
| return useMirrorExists<Favorite>('favorites', | |
| (f) => f.resourceType === resourceType && f.resourceId === resourceId && !f.deletedAt, | |
| [resourceType, resourceId]) | |
| } | |
| ``` | |
| #### Example 2: Check If Feature Flag Is Enabled (`use-feature-flags.ts`) | |
| **Current implementation** — re-renders on ANY flag change: | |
| ```typescript | |
| export function useFeatureFlagEnabled(slug: string): boolean { | |
| const flags = useFeatureFlags() | |
| const normalizedSlug = useMemo(() => slug.toLowerCase(), [slug]) | |
| return useMemo(() => { | |
| return flags.some( | |
| (flag) => flag.slug === normalizedSlug && !flag.deletedAt && flag.status === 'active' | |
| ) | |
| }, [flags, normalizedSlug]) | |
| } | |
| ``` | |
| **With `useMirrorExists`** — only re-renders when flag enabled status changes: | |
| ```typescript | |
| export function useFeatureFlagEnabled(slug: string): boolean { | |
| const normalizedSlug = slug.toLowerCase() | |
| return useMirrorExists<FeatureFlag>('feature_flags', | |
| (flag) => flag.slug === normalizedSlug && !flag.deletedAt && flag.status === 'active', | |
| [normalizedSlug]) | |
| } | |
| ``` | |
| #### Example 3: Check If Project Is Shared With Team (`use-team-project-access.ts`) | |
| **Current implementation** — re-renders on ANY grant change: | |
| ```typescript | |
| export function useIsProjectSharedWithTeam(projectId: string, teamId: string): boolean { | |
| const grants = useGlobalMirrorList<ResourceAccessGrant>('resource-access-grants') | |
| return useMemo(() => { | |
| return grants.some( | |
| (grant) => | |
| grant.actorType === 'team' && | |
| grant.actorId === teamId && | |
| grant.resourceType === 'project' && | |
| grant.resourceId === projectId && | |
| grant.status === 'active' | |
| ) | |
| }, [grants, projectId, teamId]) | |
| } | |
| ``` | |
| **With `useMirrorExists`** — only re-renders when shared status changes: | |
| ```typescript | |
| export function useIsProjectSharedWithTeam(projectId: string, teamId: string): boolean { | |
| return useMirrorExists<ResourceAccessGrant>('resource-access-grants', | |
| (g) => g.actorType === 'team' && g.actorId === teamId && | |
| g.resourceType === 'project' && g.resourceId === projectId && g.status === 'active', | |
| [projectId, teamId]) | |
| } | |
| ``` | |
| #### Example 4: Check If Project Has Messages (`use-project-has-messages.ts`) | |
| **Current implementation** — re-renders on any thread change: | |
| ```typescript | |
| export function useProjectHasMessages(projectThreads: Thread[]): boolean { | |
| return useMemo(() => projectThreads.some((thread) => (thread.messageCount ?? 0) > 0), [projectThreads]) | |
| } | |
| ``` | |
| **With `useMirrorExists`** — only re-renders when boolean changes: | |
| ```typescript | |
| export function useProjectHasMessages(projectId: string): boolean { | |
| return useMirrorExists<Thread>('threads', | |
| (t) => t.projectId === projectId && (t.messageCount ?? 0) > 0, | |
| [projectId]) | |
| } | |
| ``` | |
| --- | |
| ## 3. `useMirrorFind<T>` — Find Single Matching Item | |
| Returns the first item matching a filter, or `null`. Only re-renders when the matched item changes (by ID). | |
| ### Signature | |
| ```typescript | |
| function useMirrorFind<T extends Resource>( | |
| resourceType: ResourceType, | |
| filterFn: (item: T) => boolean, | |
| memoizeDeps: unknown[] | |
| ): T | null | |
| ``` | |
| ### Real-World Examples | |
| #### Example 1: Find Current Workspace (`use-workspaces.ts`) | |
| **Current implementation** — re-renders on ANY workspace change: | |
| ```typescript | |
| export function useCurrentWorkspace() { | |
| const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom) | |
| const allWorkspaces = useGlobalMirrorList<Workspace>('workspaces') | |
| const workspace = allWorkspaces.find((ws) => ws.id === activeWorkspaceId) | |
| // ... | |
| return workspace | |
| } | |
| ``` | |
| **With `useMirrorFind`** — only re-renders when the matched workspace changes: | |
| ```typescript | |
| export function useCurrentWorkspace() { | |
| const activeWorkspaceId = useAtomValue(activeWorkspaceIdAtom) | |
| return useMirrorFind<Workspace>('workspaces', | |
| (ws) => ws.id === activeWorkspaceId, | |
| [activeWorkspaceId]) | |
| } | |
| ``` | |
| #### Example 2: Find User's Workspace Membership (`use-workspaces.ts`) | |
| **Current implementation** — re-renders on ANY member change: | |
| ```typescript | |
| export function useIsWorkspaceGuest(workspaceId: string | null): boolean { | |
| const [authUser] = useAtom(authUserAtom) | |
| const { data: members } = useWorkspaceMembers(workspaceId) | |
| const currentUserMember = members.find( | |
| (m) => m.userId === authUser?.id && m.workspaceId === workspaceId | |
| ) | |
| return currentUserMember?.role === 'workspace_guest' | |
| } | |
| ``` | |
| **With `useMirrorFind`** — only re-renders when the user's membership changes: | |
| ```typescript | |
| export function useIsWorkspaceGuest(workspaceId: string | null): boolean { | |
| const [authUser] = useAtom(authUserAtom) | |
| const currentUserMember = useMirrorFind<WorkspaceMember>('workspace-members', | |
| (m) => m.userId === authUser?.id && m.workspaceId === workspaceId, | |
| [authUser?.id, workspaceId]) | |
| return currentUserMember?.role === 'workspace_guest' | |
| } | |
| ``` | |
| #### Example 3: Find Favorite for Resource (`use-favorites.ts`) | |
| **Current implementation** — re-renders on ANY favorite change: | |
| ```typescript | |
| export function useToggleFavorite() { | |
| const allFavorites = useGlobalMirrorList<Favorite>('favorites') | |
| // ... | |
| return useMutation({ | |
| mutationFn: async ({ resourceType, resourceId, isFavorited }) => { | |
| if (isFavorited) { | |
| const fav = allFavorites.find( | |
| (f) => f.resourceType === resourceType && f.resourceId === resourceId && !f.deletedAt | |
| ) | |
| // ... | |
| } | |
| } | |
| }) | |
| } | |
| ``` | |
| **With `useMirrorFind`** — only re-renders when the specific favorite changes: | |
| ```typescript | |
| export function useFavorite(resourceType: string, resourceId: string) { | |
| return useMirrorFind<Favorite>('favorites', | |
| (f) => f.resourceType === resourceType && f.resourceId === resourceId && !f.deletedAt, | |
| [resourceType, resourceId]) | |
| } | |
| ``` | |
| #### Example 4: Find Team/Workspace for Grant Display (`use-project-members.ts`) | |
| **Current implementation** — re-renders on ANY workspace/team change: | |
| ```typescript | |
| const workspaceGrants = useMemo(() => { | |
| return workspaceGrantsRaw.map((grant: any) => { | |
| const workspace = allWorkspaces?.find((ws: any) => ws.id === grant.actorId) | |
| return { | |
| workspaceName: workspace?.name || 'Unknown Workspace', | |
| // ... | |
| } | |
| }) | |
| }, [workspaceGrantsRaw, allWorkspaces]) | |
| ``` | |
| **With `useMirrorFind`** (for single lookups): | |
| ```typescript | |
| function useWorkspaceNameForGrant(actorId: string) { | |
| const workspace = useMirrorFind<Workspace>('workspaces', | |
| (ws) => ws.id === actorId, [actorId]) | |
| return workspace?.name || 'Unknown Workspace' | |
| } | |
| ``` | |
| --- | |
| ## Implementation Details | |
| ### Proposed Implementation | |
| ```typescript | |
| // useMirrorCount | |
| export function useMirrorCount<T extends Resource>( | |
| resourceType: ResourceType, | |
| filterFn: (item: T) => boolean, | |
| memoizeDeps: unknown[] | |
| ): number { | |
| const context = useMirrorContext() | |
| const mirror = findMirrorWithCollection(context, resourceType) | |
| if (!mirror) { | |
| throw new Error(`No mirror in hierarchy has collection '${resourceType}' registered`) | |
| } | |
| if (!mirror.isResourceInitialized(resourceType)) { | |
| mirror.ensureInitialized(resourceType) | |
| } | |
| const filterRef = useRef(filterFn) | |
| filterRef.current = filterFn | |
| const depsKey = JSON.stringify(memoizeDeps) | |
| const countAtom = useMemo(() => { | |
| const baseAtom = getResourceListAtom(mirror.getKey(), resourceType) | |
| return selectAtom( | |
| baseAtom, | |
| (items) => (items as T[]).filter(filterRef.current).length, | |
| (a, b) => a === b // Simple number equality | |
| ) | |
| }, [mirror, resourceType, depsKey]) | |
| return useAtomValue(countAtom) | |
| } | |
| // useMirrorExists | |
| export function useMirrorExists<T extends Resource>( | |
| resourceType: ResourceType, | |
| filterFn: (item: T) => boolean, | |
| memoizeDeps: unknown[] | |
| ): boolean { | |
| const context = useMirrorContext() | |
| const mirror = findMirrorWithCollection(context, resourceType) | |
| if (!mirror) { | |
| throw new Error(`No mirror in hierarchy has collection '${resourceType}' registered`) | |
| } | |
| if (!mirror.isResourceInitialized(resourceType)) { | |
| mirror.ensureInitialized(resourceType) | |
| } | |
| const filterRef = useRef(filterFn) | |
| filterRef.current = filterFn | |
| const depsKey = JSON.stringify(memoizeDeps) | |
| const existsAtom = useMemo(() => { | |
| const baseAtom = getResourceListAtom(mirror.getKey(), resourceType) | |
| return selectAtom( | |
| baseAtom, | |
| (items) => (items as T[]).some(filterRef.current), | |
| (a, b) => a === b // Simple boolean equality | |
| ) | |
| }, [mirror, resourceType, depsKey]) | |
| return useAtomValue(existsAtom) | |
| } | |
| // useMirrorFind | |
| export function useMirrorFind<T extends Resource>( | |
| resourceType: ResourceType, | |
| filterFn: (item: T) => boolean, | |
| memoizeDeps: unknown[] | |
| ): T | null { | |
| const context = useMirrorContext() | |
| const mirror = findMirrorWithCollection(context, resourceType) | |
| if (!mirror) { | |
| throw new Error(`No mirror in hierarchy has collection '${resourceType}' registered`) | |
| } | |
| if (!mirror.isResourceInitialized(resourceType)) { | |
| mirror.ensureInitialized(resourceType) | |
| } | |
| const filterRef = useRef(filterFn) | |
| filterRef.current = filterFn | |
| const depsKey = JSON.stringify(memoizeDeps) | |
| const findAtom = useMemo(() => { | |
| const baseAtom = getResourceListAtom(mirror.getKey(), resourceType) | |
| return selectAtom( | |
| baseAtom, | |
| (items) => (items as T[]).find(filterRef.current) ?? null, | |
| (a, b) => a?.id === b?.id // ID-based equality | |
| ) | |
| }, [mirror, resourceType, depsKey]) | |
| return useAtomValue(findAtom) | |
| } | |
| ``` | |
| --- | |
| ## Impact Analysis | |
| ### Files That Would Benefit | |
| | Hook | Est. Usages | Key Files | | |
| |------|-------------|-----------| | |
| | `useMirrorCount` | ~15 | `use-thread-todos.ts`, `use-notification-bell.ts`, various stats displays | | |
| | `useMirrorExists` | ~25 | `use-favorites.ts`, `use-feature-flags.ts`, `use-team-project-access.ts`, permission checks | | |
| | `useMirrorFind` | ~40 | `use-workspaces.ts`, `use-project-members.ts`, `use-current-user-workspace-role.ts` | | |
| ### Benefits | |
| 1. **Reduced re-renders** — Components only update when their derived value actually changes | |
| 2. **Cleaner code** — No need for manual `useMemo` + `.filter().length` / `.some()` / `.find()` patterns | |
| 3. **Better DX** — Intent is clear from the hook name | |
| 4. **Lower complexity** — Simpler than general-purpose selector hooks (reverted PR #3754) | |
| ### Migration Path | |
| These hooks are additive and backward-compatible. Teams can adopt incrementally: | |
| 1. Add hooks to `@/mirror/hooks.ts` | |
| 2. Export from `@/mirror/index.ts` | |
| 3. Refactor high-impact files first (feature flags, favorites, workspace lookups) | |
| 4. Gradually migrate remaining usages | |
| --- | |
| ## Summary | |
| These three convenience hooks cover ~80% of the re-render optimization opportunities found in the codebase without the complexity of general-purpose selectors. They're focused, easy to understand, and solve real performance issues observed in production. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment