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

zustand-pro

Zustand v5状态管理适用于Next.js 16。包含存储模式、中间件(持久化/immer/开发者工具)、SSR水合、CV编辑器多步骤向导,以及20个记录的错误。触发词:zustand, store, 状态管理, useState替代, 全局状态, 持久化, immer。

person作者: jakexiaohubgithub

Zustand Pro -- State Management for Next.js 16

When to Use

Trigger on any mention of: zustand, global state, store, state management, persist middleware, immer, devtools, CV editor state, multi-step form state, undo/redo.

Reference Files

Load the relevant reference file(s) based on the task:

| File | Lines | Content | |------|-------|---------| | references/setup.md | ~65 | Version compatibility matrix (v4/v5), breaking changes, React 19 notes, install, project structure | | references/core-api.md | ~100 | create, createStore, useStore, setState, subscribe, selectors | | references/nextjs-patterns.md | ~190 | 3 patterns: global store, per-request context provider (SSR), hydration-safe persist, server component rules | | references/typescript.md | ~115 | Basic typed store, middleware typing, slices pattern (large stores), typed selectors | | references/middleware.md | ~195 | persist, devtools, immer, subscribeWithSelector, combine, composition order | | references/errors-001-010.md | ~200 | ZST-001 to ZST-010: hydration, infinite re-render, server component, state sync, TS middleware, SSR storage, useShallow, devtools, persist schema, async actions | | references/errors-011-020.md | ~175 | ZST-011 to ZST-020: missing provider, immer import, fast refresh, slices+middleware TS, persist+immer merge, React Compiler naming, subscribeWithSelector, setState replace, window undefined, stale closure | | references/cviet-patterns.md | ~190 | Full multi-step CV editor store with devtools+subscribeWithSelector+immer, navigation, CRUD for all CV sections, AI patch, saving | | references/cviet-hooks.md | ~170 | Auto-save with debounce, undo/redo (zundo), AI panel integration, unsaved changes warning | | references/performance.md | ~130 | Atomic selectors, useShallow, stable action refs, transient updates, computed/derived state, selector extraction | | references/testing.md | ~155 | Jest auto-reset mock, Vitest auto-reset mock, store direct testing, component testing, pre-set state testing | | references/anti-patterns.md | ~170 | 6 anti-patterns (server component store, full store select, store in component, direct mutation, server state, missing useShallow) + 3 gotchas (middleware order, Set/Map persist, getState reactivity) | | references/ecosystem.md | ~85 | Zustand vs Redux vs Jotai comparison, why Zustand for CViet, third-party middleware (zundo, zustand-computed, zukeeper), key links, quick reference card |

Error Quick Lookup

| ID | Error | Fix | |----|-------|-----| | ZST-001 | Hydration mismatch with persist | skipHydration + useHydratedStore hook | | ZST-002 | Infinite re-render (v5) | Use useShallow or stable selector | | ZST-003 | Hook in Server Component | Move to Client Component with 'use client' | | ZST-004 | State not updating across components | Immutable update or check import path | | ZST-005 | TypeScript error with middleware | Use curried create<Type>()(...) form | | ZST-006 | Persist storage unavailable (SSR) | Guard with typeof window check | | ZST-007 | useShallow import not found | Import from zustand/shallow (v5) | | ZST-008 | DevTools not showing store | Add devtools middleware with name | | ZST-009 | Persist overwrites new fields | Add version + migrate to persist config | | ZST-010 | Async action not updating | Use set() after await, not direct mutation | | ZST-011 | Cannot read getState (undefined) | Wrap component tree in Provider | | ZST-012 | Immer not working | Import from zustand/middleware/immer | | ZST-013 | Multiple store instances (dev) | Expected in dev; use persist for preservation | | ZST-014 | TS error with slices + middleware | Declare middleware mutators in StateCreator | | ZST-015 | Persist + immer deep merge conflict | Custom merge function with structuredClone | | ZST-016 | React Compiler use prefix warning | Name stores useXxxStore | | ZST-017 | subscribeWithSelector fires always | Provide equalityFn or stable selector | | ZST-018 | setState replace missing fields (v5) | Provide ALL fields or use partial update | | ZST-019 | "window is not defined" | Guard persist storage with typeof window | | ZST-020 | Stale closure in useEffect | Use subscribe or getState() in effect |

Key Patterns

Store Creation (Next.js 16)

// src/stores/cv-editor-store.ts
'use client'  // Not needed for store file, but consumers must be client

import { create } from 'zustand'
import { persist, devtools, immer } from 'zustand/middleware'

// Middleware order matters: devtools(persist(immer(...)))

Hydration-Safe Persist

import { create } from 'zustand'
import { persist } from 'zustand/middleware'

const useStore = create(persist((...) => ({...}), {
  name: 'cv-draft',
  skipHydration: true,  // Critical for Next.js SSR
}))

// In client component:
useEffect(() => { useStore.persist.rehydrate() }, [])

useShallow for Multi-Value Selectors

import { useShallow } from 'zustand/shallow'

const { name, email } = useStore(
  useShallow((s) => ({ name: s.name, email: s.email }))
)

Middleware Composition Order

devtools -> subscribeWithSelector -> persist -> immer -> stateCreator