Install
openclaw skills install fec-state-managementUse when choosing, implementing, reviewing, or refactoring frontend state ownership across React, Vue, Next.js, Nuxt, URL state, server state, form state, browser persistence, or global stores. Prefer narrower skills for TanStack Query cache details, browser storage persistence, or form validation internals; Chinese triggers include 状态管理, 状态归属, store 选型.
openclaw skills install fec-state-management为前端状态确定清晰归属,避免全局 store 膨胀、重复缓存和派生状态同步错误。
不要先选 Redux、Zustand、Pinia 或 Context。先把每个状态标到唯一归属,再决定工具。
| 状态类型 | 典型例子 | 默认归属 |
|---|---|---|
| 本地 UI 状态 | 弹窗开关、tab、展开行、hover 编辑态 | 组件内 state / ref |
| 表单状态 | 输入值、脏字段、校验错误、提交中 | 表单库或表单组件 |
| 服务端状态 | 列表、详情、分页结果、远程错误 | 请求缓存库 |
| URL 状态 | 搜索词、筛选、排序、页码、选中 tab | 路由参数 / search params |
| 全局客户端状态 | 登录用户、主题、权限快照、购物车草稿 | 全局 store |
| 浏览器持久化状态 | 跨刷新保留的草稿、偏好、离线队列 | 存储层 + 状态适配器 |
type ReportsStateMap = {
search: "url";
selectedReportId: "url";
reports: "server-state-cache";
isFilterPanelOpen: "local-ui";
draftColumns: "browser-persistence";
};
可从 props、server state、URL 或已有 state 推导出来的值,不要另存一份。
interface Invoice {
id: string;
status: "draft" | "sent" | "paid";
}
function InvoiceList({ invoices }: { invoices: Invoice[] }) {
const paidInvoices = invoices.filter((invoice) => invoice.status === "paid");
return <span>{paidInvoices.length}</span>;
}
React 中优先本地化和组合;只有跨页面、跨 feature 或需要统一动作时才引入全局 store。
import { create } from "zustand";
interface WorkspaceState {
activeWorkspaceId: string | null;
setActiveWorkspaceId: (workspaceId: string) => void;
}
export const useWorkspaceStore = create<WorkspaceState>((set) => ({
activeWorkspaceId: null,
setActiveWorkspaceId: (activeWorkspaceId) => set({ activeWorkspaceId }),
}));
决策顺序:
useState / useReducer。Vue 3 中,局部跨层级传递使用 provide/inject;全局业务状态使用 Pinia 或项目既有 store。
import { computed, readonly, ref } from "vue";
import { defineStore } from "pinia";
export const useSessionStore = defineStore("session", () => {
const userId = ref<string | null>(null);
const isSignedIn = computed(() => userId.value !== null);
function signIn(nextUserId: string) {
userId.value = nextUserId;
}
return {
userId: readonly(userId),
isSignedIn,
signIn,
};
});
重构状态时先做状态清单,再逐步移动读写入口。每一步都应保持行为可验证。
interface StateMigrationItem {
name: string;
currentOwner: "component" | "context" | "store" | "query-cache" | "url";
targetOwner: "component" | "context" | "store" | "query-cache" | "url";
verification: string;
}
const migrationPlan: StateMigrationItem[] = [
{
name: "dashboard filters",
currentOwner: "store",
targetOwner: "url",
verification: "refreshing the page preserves filters through search params",
},
];
需要 Store 形状示例、选择器模式、URL 状态同步、持久化适配器、SSR 边界或审查清单时,加载 references/state-patterns.md。
产出状态归属清单、选型理由、store/API 边界和验证步骤。实现时应保留 loading/error/empty、刷新、回退、跨路由和权限变化等关键行为。