一键导入
zustand-v5
Zustand v5 state management reference. Auto-loads when working with zustand stores, create, createStore, useShallow, persist, devtools, immer middleware.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Zustand v5 state management reference. Auto-loads when working with zustand stores, create, createStore, useShallow, persist, devtools, immer middleware.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use when debugging Praxis agent-harness failures: tool calls leaked into chat, raw <invoke> markup, [object Object] rendering, tool.dispatch.error, sub-agent stalls, IPC stream failures, React renderer crashes, persistence/FK failures, course-create drafting failures, or student-simulation/browser mismatches. Guides agents through local evidence capture, replay/simulation commands, owner routing, and substrate item filing.
Project code patterns and conventions. Auto-loads when implementing, designing, verifying, or reviewing code. Provides detailed pattern definitions with code examples.
Project-specific refactor conventions for Praxis. Auto-loads when agile-workflow refactor-design scans or designs refactors. Extends refactor-design's default smell scan with team-confirmed style and structural preferences; does not replace the defaults and does not create standalone plan docs.
Reference for the Gemini / Antigravity engine path in Praxis. Auto-loads when an agent is touching Gemini, Antigravity, `@ai-sdk/google`, `direct.google`, `google-antigravity`, `GEMINI_API_KEY`, `GOOGLE_GENERATIVE_AI_API_KEY`, or considering a new engine adapter for Google models. Captures the policy + technical constraints found in `docs/research/gemini-subscription-engine.md` (2026-05-19) so future agents don't re-discover them.
officeparser v6 reference for parsing .pptx (and other office formats) into a typed AST with extracted attachments. Triggers on imports from `officeparser`, mentions of `OfficeParser.parseOffice`, `OfficeParserAST`, `OfficeAttachment`, `OfficeContentNode`, or work on PowerPoint / PPTX ingestion in `packages/tools/src/runtime/ingestion/`.
Drizzle ORM v0.45+ reference for PostgreSQL. Auto-loads when working with drizzle-orm, pgTable, schema definitions, drizzle queries, migrations, drizzle-kit.
| name | zustand-v5 |
| description | Zustand v5 state management reference. Auto-loads when working with zustand stores, create, createStore, useShallow, persist, devtools, immer middleware. |
| user-invocable | false |
Version: 5.x Docs: https://zustand.docs.pmnd.rs/
// Core store creation
import { create } from 'zustand'
// Vanilla store (non-React)
import { createStore } from 'zustand/vanilla'
import { useStore } from 'zustand'
// Middleware
import { devtools, persist, createJSONStorage } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'
// Shallow equality for multiple selectors
import { useShallow } from 'zustand/react/shallow'
// Types
import type { StoreApi, UseBoundStore, StateCreator } from 'zustand'
interface State {
value: number
increment: () => void
decrement: () => void
}
const useStore = create<State>((set) => ({
value: 0,
increment: () => set((state) => ({ value: state.value + 1 })),
decrement: () => set({ value: 0 })
}))
const useStore = create<State>()(
devtools(
persist(
(set) => ({ /* state */ }),
{ name: 'storage-key' }
),
{ name: 'DevTools Name' }
)
)
// Select single value
const value = useStore((state) => state.value)
// Select multiple with shallow equality
const { value, increment } = useStore(
useShallow((state) => ({ value: state.value, increment: state.increment }))
)
// Entire store
const state = useStore()
set((state) => ({ count: state.count + 1 })) // Updater function
set({ count: 0 }) // Direct merge
set({ count: 0 }, true) // Replace (don't merge)
const store = createStore<State>((set) => ({ /* state */ }))
store.getState()
store.setState({ value: 5 })
store.subscribe((state) => console.log(state))
// Use in React
function Component() {
const value = useStore(store, (state) => state.value)
return <div>{value}</div>
}
persist(
(set) => ({ /* state */ }),
{
name: 'storage-key',
storage: createJSONStorage(() => localStorage),
partialize: (state) => ({ field: state.field }),
merge: (persisted, current) => ({ ...current, ...persisted }),
onRehydrateStorage: (state) => (state, error) => {
if (error) console.error(error)
}
}
)
devtools(
(set) => ({ /* state */ }),
{ name: 'Store Name' }
)
immer((set) => ({
user: { name: 'John' },
updateName: (name: string) => set((state) => {
state.user.name = name // Mutate directly
})
}))
Double parentheses with middleware — create<State>()() when using middleware, single create<State>() without.
useShallow for multiple selections — Without it, selecting multiple values causes re-renders on any state change. v5 changed default selector behavior to referential equality.
Don't nest immer inside devtools — Use devtools(immer(...)), not immer(devtools(...)). Immer must be innermost.
Middleware order matters — Recommended: devtools(persist(immer(...))). Outer middleware wraps inner.
set() merges by default — Use set(state, true) to replace instead of merge.
Stores must be defined outside components — Defining stores inside components causes re-creation on every render.
Selectors must be stable — Returning new object/array references on every call causes infinite loops. Use useShallow for derived objects.
Actions are stable — Functions in the store don't change reference, so selecting them doesn't cause re-renders.
persist hydration is async — State may not be loaded immediately. Use onRehydrateStorage to detect completion or track hasHydrated flag.
partialize doesn't affect initial state — Non-persisted fields still need defaults in the store creator.
merge() receives partial persisted state — Always provide fallbacks for missing fields.
interface State {
hasHydrated: boolean
setHasHydrated: (hydrated: boolean) => void
// ... other state
}
create<State>()(
persist(
(set) => ({
hasHydrated: false,
setHasHydrated: (hydrated) => set({ hasHydrated: hydrated }),
// ... other state
}),
{
name: 'my-storage-key',
partialize: (state) => ({ /* only persisted fields */ }),
onRehydrateStorage: () => (state) => {
state?.setHasHydrated(true)
}
}
)
)
persist(
(set) => ({ /* state */ }),
{
merge: (persistedState, currentState) => {
const persisted = persistedState as Partial<State> | undefined
return {
...currentState,
value: clampValue(persisted?.value ?? DEFAULT_VALUE)
}
}
}
)
// Export convenience selectors
export const useStatus = (topic: string) =>
useStore((state) => state.getStatus(topic))
export const useItems = () =>
useStore((state) => state.items)
// Bad - recreates store on every render
function Component() {
const useStore = create<State>((set) => ({ /* state */ }))
}
// Good - define at module level
const useStore = create<State>((set) => ({ /* state */ }))
function Component() {
const value = useStore((state) => state.value)
}
// Bad - re-renders on any state change
const { value, count } = useStore((state) => ({
value: state.value,
count: state.count
}))
// Good - only re-renders when value or count change
const { value, count } = useStore(
useShallow((state) => ({ value: state.value, count: state.count }))
)
// Bad - creates new array every time
const items = useStore((state) => state.items.filter(x => x.active))
// Good - compute in component body
function Component() {
const items = useStore((state) => state.items)
const activeItems = items.filter(x => x.active)
}
// Bad - redundant state
const useStore = create((set) => ({
firstName: 'John',
lastName: 'Doe',
fullName: 'John Doe'
}))
// Good - compute in selector
const useStore = create((set) => ({
firstName: 'John',
lastName: 'Doe'
}))
const fullName = useStore((state) => `${state.firstName} ${state.lastName}`)
// Bad - immer outside devtools breaks debugging
immer(devtools((set) => ({ /* state */ })))
// Good - immer innermost
devtools(persist(immer((set) => ({ /* state */ }))))
// Bad - direct mutation without immer
set((state) => {
state.user.name = name
return state
})
// Good - with immer
immer((set) => ({
updateName: (name) => set((state) => {
state.user.name = name
})
}))
// Also good - without immer, return new object
set((state) => ({
user: { ...state.user, name }
}))