Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save matthew-gerstman/eafdcd797c176547b70f4af57b6550a2 to your computer and use it in GitHub Desktop.

Select an option

Save matthew-gerstman/eafdcd797c176547b70f4af57b6550a2 to your computer and use it in GitHub Desktop.
Mirror Convenience Hooks Proposal
# 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