state-architecture

Designs scalable state management architecture for complex frontend apps. Covers store slicing, server vs client state separation, optimistic updates, cross-tab sync, and state machine patterns. Use when 状态管理, store 设计, Zustand, Redux, 状态架构, 乐观更新, 状态机, or state design.

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "state-architecture" with this command: npx skills add wangzhiming1999/state-architecture

状态管理架构(State Architecture)

为复杂前端应用设计可扩展的状态管理方案——不只是"用哪个库",而是如何切分、如何同步、如何避免状态腐化。

触发场景

  • 「状态管理怎么设计」「store 越来越乱」「组件间状态共享」
  • 「乐观更新怎么做」「状态和服务端不同步」「跨 tab 同步」
  • 「要不要用状态机」「这个流程状态太复杂了」
  • 新项目技术选型,或现有状态管理出现明显问题

核心原则

状态分类是第一步,选库是第二步。

状态类型定义推荐管理方式
服务端状态来自 API,有缓存/失效/同步问题React Query / SWR
全局 UI 状态跨组件共享,与服务端无关(主题、侧边栏、弹层)Zustand / Jotai
本地 UI 状态单组件内部(输入值、hover、展开收起)useState / useReducer
URL 状态可分享、可回退(筛选、分页、tab)URL 参数(nuqs / useSearchParams)
表单状态输入、校验、提交React Hook Form

最常见的错误:把服务端状态放进全局 store,然后手动维护缓存和同步——这是 React Query 要解决的问题,不要重复造轮子。


执行流程

1. 先诊断,再设计

用户说「状态管理有问题」时,先问清楚症状:

症状根因方向
「store 里全是接口数据」服务端状态混入全局 store迁移到 React Query,store 只留 UI 状态
「A 组件改了,B 组件不更新」状态没有提升到公共祖先,或订阅粒度太粗检查状态归属,或细化 selector
「store 越来越大,不知道谁在用」缺乏 slice 边界,状态所有权不清按功能域切分 slice,明确所有权
「乐观更新回滚逻辑很乱」没有统一的乐观更新模式用 React Query 的 onMutate/onError 模式
「这个流程有 10 种状态,if/else 写不下去了」需要状态机用 XState 或手写状态机

2. 状态归属决策树

拿到一个状态,按这个顺序判断放哪里:

这个状态是从 API 来的吗?
├── 是 → 用 React Query/SWR,不要放 store
└── 否 → 只有一个组件用吗?
    ├── 是 → useState,不要提升
    └── 否 → 需要在 URL 里体现吗(可分享/可回退)?
        ├── 是 → URL 参数
        └── 否 → 全局 store(Zustand/Jotai)

原则:状态尽量下沉,能放组件内就不放 store,能放 URL 就不放 store。

3. Zustand store 设计规范

按功能域切 slice,不要一个大 store:

// ❌ 错:一个 store 装所有东西
const useStore = create((set) => ({
  user: null,
  theme: 'light',
  sidebarOpen: false,
  notifications: [],
  cartItems: [],
  // ...越来越多
}))

// ✅ 对:按功能域切分
const useUIStore = create((set) => ({
  theme: 'light',
  sidebarOpen: false,
  setTheme: (theme) => set({ theme }),
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
}))

const useNotificationStore = create((set) => ({
  items: [],
  add: (item) => set((s) => ({ items: [...s.items, item] })),
  dismiss: (id) => set((s) => ({ items: s.items.filter(i => i.id !== id) })),
}))

selector 细化,避免无效重渲染:

// ❌ 错:订阅整个 store,任何字段变化都重渲染
const { theme, sidebarOpen } = useUIStore()

// ✅ 对:只订阅需要的字段
const theme = useUIStore((s) => s.theme)
const sidebarOpen = useUIStore((s) => s.sidebarOpen)

4. 乐观更新的标准模式

用 React Query 的 useMutation + onMutate/onError/onSettled

const mutation = useMutation({
  mutationFn: updateTodo,
  onMutate: async (newTodo) => {
    // 1. 取消正在进行的请求,避免覆盖乐观更新
    await queryClient.cancelQueries({ queryKey: ['todos'] })
    // 2. 保存快照用于回滚
    const snapshot = queryClient.getQueryData(['todos'])
    // 3. 乐观更新缓存
    queryClient.setQueryData(['todos'], (old) =>
      old.map(t => t.id === newTodo.id ? { ...t, ...newTodo } : t)
    )
    return { snapshot }
  },
  onError: (err, newTodo, context) => {
    // 4. 失败时回滚
    queryClient.setQueryData(['todos'], context.snapshot)
  },
  onSettled: () => {
    // 5. 无论成功失败,重新拉取确保数据一致
    queryClient.invalidateQueries({ queryKey: ['todos'] })
  },
})

不要自己在 store 里手写乐观更新逻辑——React Query 的这套模式处理了所有边界情况。

5. 状态机:什么时候用,怎么用

需要状态机的信号:

  • 一个流程有 5+ 种状态,且状态之间有明确的转换规则
  • 出现了「在状态 A 下不能做 B」这类约束
  • 状态转换有副作用(发请求、记日志)
  • 团队经常讨论「现在到底是什么状态」

轻量状态机(不引入 XState):

type State =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: Data }
  | { status: 'error'; error: Error }

type Event =
  | { type: 'FETCH' }
  | { type: 'SUCCESS'; data: Data }
  | { type: 'ERROR'; error: Error }
  | { type: 'RESET' }

function reducer(state: State, event: Event): State {
  switch (state.status) {
    case 'idle':
      if (event.type === 'FETCH') return { status: 'loading' }
      return state
    case 'loading':
      if (event.type === 'SUCCESS') return { status: 'success', data: event.data }
      if (event.type === 'ERROR') return { status: 'error', error: event.error }
      return state
    case 'success':
    case 'error':
      if (event.type === 'RESET') return { status: 'idle' }
      return state
  }
}

用 XState 的时机: 流程有并行状态、历史状态、延迟转换,或需要可视化状态图给产品/测试看。

6. 跨 Tab 状态同步

需要多 tab 共享状态时(如登录态、购物车):

// 用 BroadcastChannel(现代浏览器原生支持)
const channel = new BroadcastChannel('app-state')

// 发送
channel.postMessage({ type: 'CART_UPDATED', payload: cartItems })

// 接收
channel.onmessage = (event) => {
  if (event.data.type === 'CART_UPDATED') {
    useCartStore.setState({ items: event.data.payload })
  }
}

// 或用 Zustand 的 persist + storage event 监听 localStorage 变化

常见反模式

反模式问题正确做法
把 API 响应直接存 store手动维护缓存,容易不同步用 React Query
store 里存派生数据数据源不唯一,容易不一致用 selector 计算派生数据
组件里直接 store.getState()绕过响应式,不会触发重渲染用 hook 订阅
一个 action 改多个 slice耦合,难以追踪每个 slice 只管自己的状态,用事件驱动跨 slice 通信
状态里存 UI 临时状态(hover、focus)不必要的全局状态用组件本地 state

输出模板

## 状态管理方案

### 状态分类
| 状态 | 类型 | 管理方式 | 理由 |
|------|------|---------|------|
| 用户信息 | 服务端状态 | React Query | 需要缓存和同步 |
| 主题 | 全局 UI | Zustand | 跨组件,与服务端无关 |
| 表单输入 | 本地 UI | useState | 单组件内部 |
| 筛选条件 | URL 状态 | useSearchParams | 可分享、可回退 |

### Store 结构
- useUIStore:主题、侧边栏、弹层
- use[Domain]Store:[具体业务域]

### 关键决策
- 乐观更新:用 React Query onMutate 模式
- 跨 Tab 同步:BroadcastChannel / localStorage event
- 复杂流程:状态机(轻量 reducer / XState)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

Spicy Ai Video

Turn a 60-second talking head clip into 1080p high-energy edited videos just by typing what you need. Whether it's turning bland footage into visually intens...

Registry SourceRecently Updated
Coding

Video Maker Fast

Get polished MP4 videos ready to post, without touching a single slider. Upload your video clips (MP4, MOV, AVI, WebM, up to 500MB), say something like "trim...

Registry SourceRecently Updated
Coding

Generation Generator

generate text prompts or clips into AI generated videos with this skill. Works with MP4, MOV, PNG, JPG files up to 500MB. marketers, content creators, social...

Registry SourceRecently Updated
Coding

Editor On Android

Get edited MP4 clips ready to post, without touching a single slider. Upload your video clips (MP4, MOV, AVI, WebM, up to 500MB), say something like "trim th...

Registry SourceRecently Updated