返回 Skill 列表
extension
分类: 开发与工程无需 API Key

client-state-management

在React应用程序中实现客户端状态管理的指南。在构建状态架构、选择状态库(Context、Zustand、Redux、Jotai)、实现缓存策略(React Query、SWR)、乐观更新、状态持久化或优化重新渲染时使用。当有关于全局与局部状态、状态规范化或选择器模式的问题时触发。

person作者: jakexiaohubgithub

Client-Side State Management

Decision: Library Selection

| Need | Use | Why | |------|-----|-----| | Simple shared state, <5 consumers | Context API | Zero dependencies, built-in | | Medium complexity, performance matters | Zustand | 1.5kb, no boilerplate, auto re-render optimization | | Large app, strict patterns needed | Redux Toolkit | DevTools, middleware ecosystem, time-travel | | Fine-grained reactivity, atoms | Jotai | Bottom-up, minimal re-renders, composable | | Server state (fetching/caching) | React Query or SWR | Deduplication, background refresh, cache |

Decision: Global vs Local State

Keep Local (useState/useReducer):

  • Form input values before submission
  • UI state (open/closed, hover, focus)
  • Component-specific loading/error states

Promote to Global:

  • User session/auth
  • Theme/locale preferences
  • Data shared across 3+ unrelated components
  • State that must survive navigation

State Normalization

Flatten nested data to avoid update complexity:

// Bad: nested
{ posts: [{ id: 1, author: { id: 1, name: 'Jo' }, comments: [...] }] }

// Good: normalized
{
  posts: { byId: { 1: { id: 1, authorId: 1, commentIds: [1,2] } }, allIds: [1] },
  users: { byId: { 1: { id: 1, name: 'Jo' } } },
  comments: { byId: { 1: {...}, 2: {...} } }
}

Optimistic Updates Pattern

Update UI immediately, rollback on error:

// React Query
useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    await queryClient.cancelQueries({ queryKey: ['todos'] })
    const previous = queryClient.getQueryData(['todos'])
    queryClient.setQueryData(['todos'], old => [...old, newTodo])
    return { previous }
  },
  onError: (err, newTodo, context) => {
    queryClient.setQueryData(['todos'], context.previous)
  },
  onSettled: () => queryClient.invalidateQueries({ queryKey: ['todos'] })
})

State Persistence

// Zustand with persist middleware
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

const useStore = create(
  persist(
    (set) => ({ theme: 'light', setTheme: (t) => set({ theme: t }) }),
    {
      name: 'app-settings',
      storage: createJSONStorage(() => localStorage), // or sessionStorage
      partialize: (state) => ({ theme: state.theme }) // persist only specific keys
    }
  )
)

Performance: Selector Patterns

Prevent unnecessary re-renders by selecting only needed state:

// Zustand - component only re-renders when `count` changes
const count = useStore((state) => state.count)

// Jotai - selectAtom for derived slices
const nameAtom = selectAtom(userAtom, (user) => user.name)

// React Query - select option
useQuery({
  queryKey: ['user'],
  queryFn: fetchUser,
  select: (data) => data.name // component only gets name
})

Reference Files

Performance Checklist

  • [ ] Selectors return minimal data needed
  • [ ] Memoize selectors with expensive computations
  • [ ] Split stores by domain (don't put everything in one store)
  • [ ] Use shallow comparison for object selections in Zustand
  • [ ] Set appropriate staleTime/cacheTime for server state
  • [ ] Avoid storing derived state (compute from source)