zustand_state

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Zustand State Management

Zustand 状态管理

Status: Production Ready ✅ Last Updated: 2025-10-24 Latest Version: zustand@5.0.8 Dependencies: React 18+, TypeScript 5+

状态:可用于生产环境 ✅ 最后更新:2025-10-24 最新版本:zustand@5.0.8 依赖:React 18+、TypeScript 5+

Quick Start (3 Minutes)

快速开始(3分钟)

1. Install Zustand

1. 安装Zustand

bash
npm install zustand
bash
npm install zustand

or

pnpm add zustand
pnpm add zustand

or

yarn add zustand

**Why Zustand?**
- Minimal API: Only 1 function to learn (`create`)
- No boilerplate: No providers, reducers, or actions
- TypeScript-first: Excellent type inference
- Fast: Fine-grained subscriptions prevent unnecessary re-renders
- Flexible: Middleware for persistence, devtools, and more
yarn add zustand

**为什么选择Zustand?**
- 极简API:只需学习一个`create`函数
- 无冗余模板:无需Provider、Reducer或Actions模板
- TypeScript优先:出色的类型推断能力
- 高性能:细粒度订阅避免不必要的重渲染
- 灵活扩展:支持持久化、开发者工具等中间件

2. Create Your First Store (TypeScript)

2. 创建第一个Store(TypeScript)

typescript
import { create } from 'zustand'

interface BearStore {
  bears: number
  increase: (by: number) => void
  reset: () => void
}

const useBearStore = create<BearStore>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
  reset: () => set({ bears: 0 }),
}))
CRITICAL: Notice the double parentheses
create<T>()()
- this is required for TypeScript with middleware.
typescript
import { create } from 'zustand'

interface BearStore {
  bears: number
  increase: (by: number) => void
  reset: () => void
}

const useBearStore = create<BearStore>()((set) => ({
  bears: 0,
  increase: (by) => set((state) => ({ bears: state.bears + by })),
  reset: () => set({ bears: 0 }),
}))
重点注意:注意双括号
create<T>()()
的写法——这是TypeScript环境下使用中间件的必需语法。

3. Use Store in Components

3. 在组件中使用Store

tsx
import { useBearStore } from './store'

function BearCounter() {
  const bears = useBearStore((state) => state.bears)
  return <h1>{bears} around here...</h1>
}

function Controls() {
  const increase = useBearStore((state) => state.increase)
  return <button onClick={() => increase(1)}>Add bear</button>
}
Why this works:
  • Components only re-render when their selected state changes
  • No Context providers needed
  • Selector function extracts specific state slice

tsx
import { useBearStore } from './store'

function BearCounter() {
  const bears = useBearStore((state) => state.bears)
  return <h1>这里有{bears}只熊...</h1>
}

function Controls() {
  const increase = useBearStore((state) => state.increase)
  return <button onClick={() => increase(1)}>添加一只熊</button>
}
工作原理
  • 组件仅在其订阅的特定状态变更时才会重渲染
  • 无需Context Provider
  • 通过选择器函数提取特定状态片段

The 3-Pattern Setup Process

三种核心配置模式

Pattern 1: Basic Store (JavaScript)

模式1:基础Store(JavaScript)

For simple use cases without TypeScript:
javascript
import { create } from 'zustand'

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}))
When to use:
  • Prototyping
  • Small apps
  • No TypeScript in project
适用于无需TypeScript的简单场景:
javascript
import { create } from 'zustand'

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}))
适用场景
  • 快速原型开发
  • 小型应用
  • 项目未使用TypeScript

Pattern 2: TypeScript Store (Recommended)

模式2:TypeScript Store(推荐)

For production apps with type safety:
typescript
import { create } from 'zustand'

// Define store interface
interface CounterStore {
  count: number
  increment: () => void
  decrement: () => void
}

// Create typed store
const useCounterStore = create<CounterStore>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}))
Key Points:
  • Separate interface for state + actions
  • Use
    create<T>()()
    syntax (currying for middleware)
  • Full IDE autocomplete and type checking
适用于需要类型安全的生产环境应用:
typescript
import { create } from 'zustand'

// 定义Store接口
interface CounterStore {
  count: number
  increment: () => void
  decrement: () => void
}

// 创建带类型的Store
const useCounterStore = create<CounterStore>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}))
核心要点
  • 为状态和操作定义独立接口
  • 使用
    create<T>()()
    柯里化语法(为中间件兼容做准备)
  • 完整的IDE自动补全和类型检查

Pattern 3: Persistent Store

模式3:持久化Store

For state that survives page reloads:
typescript
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

interface UserPreferences {
  theme: 'light' | 'dark' | 'system'
  language: string
  setTheme: (theme: UserPreferences['theme']) => void
  setLanguage: (language: string) => void
}

const usePreferencesStore = create<UserPreferences>()(
  persist(
    (set) => ({
      theme: 'system',
      language: 'en',
      setTheme: (theme) => set({ theme }),
      setLanguage: (language) => set({ language }),
    }),
    {
      name: 'user-preferences', // unique name in localStorage
      storage: createJSONStorage(() => localStorage), // optional: defaults to localStorage
    },
  ),
)
Why this matters:
  • State automatically saved to localStorage
  • Restored on page reload
  • Works with sessionStorage too
  • Handles serialization automatically

适用于页面刷新后仍需保留的状态:
typescript
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

interface UserPreferences {
  theme: 'light' | 'dark' | 'system'
  language: string
  setTheme: (theme: UserPreferences['theme']) => void
  setLanguage: (language: string) => void
}

const usePreferencesStore = create<UserPreferences>()(
  persist(
    (set) => ({
      theme: 'system',
      language: 'en',
      setTheme: (theme) => set({ theme }),
      setLanguage: (language) => set({ language }),
    }),
    {
      name: 'user-preferences', // localStorage中的唯一标识
      storage: createJSONStorage(() => localStorage), // 可选:默认使用localStorage
    },
  ),
)
核心价值
  • 状态自动保存到localStorage
  • 页面刷新后自动恢复
  • 同样支持sessionStorage
  • 自动处理序列化逻辑

Critical Rules

关键规则

Always Do

务必遵守

✅ Use
create<T>()()
(double parentheses) in TypeScript for middleware compatibility ✅ Define separate interfaces for state and actions ✅ Use selector functions to extract specific state slices ✅ Use
set
with updater functions for derived state:
set((state) => ({ count: state.count + 1 }))
✅ Use unique names for persist middleware storage keys ✅ Handle Next.js hydration with
hasHydrated
flag pattern ✅ Use
shallow
for selecting multiple values ✅ Keep actions pure (no side effects except state updates)
✅ TypeScript环境下使用
create<T>()()
(双括号)语法,确保中间件兼容性 ✅ 为状态和操作定义独立接口 ✅ 使用选择器函数提取特定状态片段 ✅ 派生状态使用更新器函数:
set((state) => ({ count: state.count + 1 }))
✅ 持久化中间件的存储键使用唯一名称 ✅ Next.js项目中使用
hasHydrated
标志模式处理hydration ✅ 选择多个值时使用
shallow
比较 ✅ 保持操作纯函数(除状态更新外无副作用)

Never Do

切勿操作

❌ Use
create<T>(...)
(single parentheses) in TypeScript - breaks middleware types ❌ Mutate state directly:
set((state) => { state.count++; return state })
- use immutable updates ❌ Create new objects in selectors:
useStore((state) => ({ a: state.a }))
- causes infinite renders ❌ Use same storage name for multiple stores - causes data collisions ❌ Access localStorage during SSR without hydration check ❌ Use Zustand for server state - use TanStack Query instead ❌ Export store instance directly - always export the hook

❌ TypeScript环境下使用
create<T>(...)
(单括号)——会破坏中间件类型 ❌ 直接修改状态:
set((state) => { state.count++; return state })
——需使用不可变更新 ❌ 在选择器中创建新对象:
useStore((state) => ({ a: state.a }))
——会导致无限渲染 ❌ 多个Store使用相同的存储名称——会导致数据冲突 ❌ SSR期间未做hydration检查就访问localStorage ❌ 使用Zustand管理服务端状态——推荐使用TanStack Query ❌ 直接导出Store实例——始终导出Hook

Known Issues Prevention

常见问题预防

This skill prevents 5 documented issues:
本方案可预防5个已记录的常见问题:

Issue #1: Next.js Hydration Mismatch

问题1:Next.js Hydration不匹配

Error:
"Text content does not match server-rendered HTML"
or
"Hydration failed"
Source:
Why It Happens: Persist middleware reads from localStorage on client but not on server, causing state mismatch.
Prevention:
typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface StoreWithHydration {
  count: number
  _hasHydrated: boolean
  setHasHydrated: (hydrated: boolean) => void
  increase: () => void
}

const useStore = create<StoreWithHydration>()(
  persist(
    (set) => ({
      count: 0,
      _hasHydrated: false,
      setHasHydrated: (hydrated) => set({ _hasHydrated: hydrated }),
      increase: () => set((state) => ({ count: state.count + 1 })),
    }),
    {
      name: 'my-store',
      onRehydrateStorage: () => (state) => {
        state?.setHasHydrated(true)
      },
    },
  ),
)

// In component
function MyComponent() {
  const hasHydrated = useStore((state) => state._hasHydrated)

  if (!hasHydrated) {
    return <div>Loading...</div>
  }

  // Now safe to render with persisted state
  return <ActualContent />
}
错误信息
"Text content does not match server-rendered HTML"
"Hydration failed"
来源
问题原因: 持久化中间件在客户端读取localStorage,但服务端无此数据,导致状态不匹配。
解决方案
typescript
import { create } from 'zustand'
import { persist } from 'zustand/middleware'

interface StoreWithHydration {
  count: number
  _hasHydrated: boolean
  setHasHydrated: (hydrated: boolean) => void
  increase: () => void
}

const useStore = create<StoreWithHydration>()(
  persist(
    (set) => ({
      count: 0,
      _hasHydrated: false,
      setHasHydrated: (hydrated) => set({ _hasHydrated: hydrated }),
      increase: () => set((state) => ({ count: state.count + 1 })),
    }),
    {
      name: 'my-store',
      onRehydrateStorage: () => (state) => {
        state?.setHasHydrated(true)
      },
    },
  ),
)

// 在组件中使用
function MyComponent() {
  const hasHydrated = useStore((state) => state._hasHydrated)

  if (!hasHydrated) {
    return <div>加载中...</div>
  }

  // 现在可以安全渲染持久化状态
  return <ActualContent />
}

Issue #2: TypeScript Double Parentheses Missing

问题2:TypeScript双括号缺失

Error: Type inference fails,
StateCreator
types break with middleware
Why It Happens: The currying syntax
create<T>()()
is required for middleware to work with TypeScript inference.
Prevention:
typescript
// ❌ WRONG - Single parentheses
const useStore = create<MyStore>((set) => ({
  // ...
}))

// ✅ CORRECT - Double parentheses
const useStore = create<MyStore>()((set) => ({
  // ...
}))
Rule: Always use
create<T>()()
in TypeScript, even without middleware (future-proof).
错误信息:类型推断失败,使用中间件时
StateCreator
类型报错
问题原因: 柯里化语法
create<T>()()
是中间件与TypeScript推断兼容的必需条件。
解决方案
typescript
// ❌ 错误写法 - 单括号
const useStore = create<MyStore>((set) => ({
  // ...
}))

// ✅ 正确写法 - 双括号
const useStore = create<MyStore>()((set) => ({
  // ...
}))
规则:即使不使用中间件,TypeScript环境下也始终使用
create<T>()()
写法(为未来扩展做准备)。

Issue #3: Persist Middleware Import Error

问题3:持久化中间件导入错误

Error:
"Attempted import error: 'createJSONStorage' is not exported from 'zustand/middleware'"
Source: GitHub Discussion #2839
Why It Happens: Wrong import path or version mismatch between zustand and build tools.
Prevention:
typescript
// ✅ CORRECT imports for v5
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

// Verify versions
// zustand@5.0.8 includes createJSONStorage
// zustand@4.x uses different API

// Check your package.json
// "zustand": "^5.0.8"
错误信息
"Attempted import error: 'createJSONStorage' is not exported from 'zustand/middleware'"
来源:GitHub讨论 #2839
问题原因: 导入路径错误或zustand版本与构建工具不兼容。
解决方案
typescript
// ✅ v5版本正确导入
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

// 验证版本
// zustand@5.0.8包含createJSONStorage
// zustand@4.x使用不同API

// 检查package.json
// "zustand": "^5.0.8"

Issue #4: Infinite Render Loop

问题4:无限渲染循环

Error: Component re-renders infinitely, browser freezes
Source: GitHub Discussions #2642
Why It Happens: Creating new object references in selectors causes Zustand to think state changed.
Prevention:
typescript
import { shallow } from 'zustand/shallow'

// ❌ WRONG - Creates new object every time
const { bears, fishes } = useStore((state) => ({
  bears: state.bears,
  fishes: state.fishes,
}))

// ✅ CORRECT Option 1 - Select primitives separately
const bears = useStore((state) => state.bears)
const fishes = useStore((state) => state.fishes)

// ✅ CORRECT Option 2 - Use shallow for multiple values
const { bears, fishes } = useStore(
  (state) => ({ bears: state.bears, fishes: state.fishes }),
  shallow,
)
错误信息:组件无限重渲染,浏览器冻结
来源:GitHub讨论 #2642
问题原因: 在选择器中创建新对象引用会让Zustand认为状态已变更。
解决方案
typescript
import { shallow } from 'zustand/shallow'

// ❌ 错误写法 - 每次创建新对象
const { bears, fishes } = useStore((state) => ({
  bears: state.bears,
  fishes: state.fishes,
}))

// ✅ 正确写法1 - 分别选择原始值
const bears = useStore((state) => state.bears)
const fishes = useStore((state) => state.fishes)

// ✅ 正确写法2 - 选择多个值时使用shallow
const { bears, fishes } = useStore(
  (state) => ({ bears: state.bears, fishes: state.fishes }),
  shallow,
)

Issue #5: Slices Pattern TypeScript Complexity

问题5:切片模式TypeScript复杂度

Error:
StateCreator
types fail to infer, complex middleware types break
Why It Happens: Combining multiple slices requires explicit type annotations for middleware compatibility.
Prevention:
typescript
import { create, StateCreator } from 'zustand'

// Define slice types
interface BearSlice {
  bears: number
  addBear: () => void
}

interface FishSlice {
  fishes: number
  addFish: () => void
}

// Create slices with proper types
const createBearSlice: StateCreator<
  BearSlice & FishSlice,  // Combined store type
  [],                      // Middleware mutators (empty if none)
  [],                      // Chained middleware (empty if none)
  BearSlice               // This slice's type
> = (set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
})

const createFishSlice: StateCreator<
  BearSlice & FishSlice,
  [],
  [],
  FishSlice
> = (set) => ({
  fishes: 0,
  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
})

// Combine slices
const useStore = create<BearSlice & FishSlice>()((...a) => ({
  ...createBearSlice(...a),
  ...createFishSlice(...a),
}))

错误信息
StateCreator
类型推断失败,复杂中间件类型报错
问题原因: 合并多个切片时需要显式类型注解以确保中间件兼容性。
解决方案
typescript
import { create, StateCreator } from 'zustand'

// 定义切片类型
interface BearSlice {
  bears: number
  addBear: () => void
}

interface FishSlice {
  fishes: number
  addFish: () => void
}

// 创建带正确类型的切片
const createBearSlice: StateCreator<
  BearSlice & FishSlice,  // 合并后的Store类型
  [],                      // 中间件修改器(无则为空)
  [],                      // 链式中间件(无则为空)
  BearSlice               // 当前切片类型
> = (set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
})

const createFishSlice: StateCreator<
  BearSlice & FishSlice,
  [],
  [],
  FishSlice
> = (set) => ({
  fishes: 0,
  addFish: () => set((state) => ({ fishes: state.fishes + 1 })),
})

// 合并切片
const useStore = create<BearSlice & FishSlice>()((...a) => ({
  ...createBearSlice(...a),
  ...createFishSlice(...a),
}))

Middleware Configuration

中间件配置

Persist Middleware (localStorage)

持久化中间件(localStorage)

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

interface MyStore {
  data: string[]
  addItem: (item: string) => void
}

const useStore = create<MyStore>()(
  persist(
    (set) => ({
      data: [],
      addItem: (item) => set((state) => ({ data: [...state.data, item] })),
    }),
    {
      name: 'my-storage',
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({ data: state.data }), // Only persist 'data'
    },
  ),
)
typescript
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

interface MyStore {
  data: string[]
  addItem: (item: string) => void
}

const useStore = create<MyStore>()(
  persist(
    (set) => ({
      data: [],
      addItem: (item) => set((state) => ({ data: [...state.data, item] })),
    }),
    {
      name: 'my-storage',
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({ data: state.data }), // 仅持久化'data'
    },
  ),
)

Devtools Middleware (Redux DevTools)

开发者工具中间件(Redux DevTools)

typescript
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'

interface CounterStore {
  count: number
  increment: () => void
}

const useStore = create<CounterStore>()(
  devtools(
    (set) => ({
      count: 0,
      increment: () =>
        set(
          (state) => ({ count: state.count + 1 }),
          undefined,
          'counter/increment', // Action name in DevTools
        ),
    }),
    { name: 'CounterStore' }, // Store name in DevTools
  ),
)
typescript
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'

interface CounterStore {
  count: number
  increment: () => void
}

const useStore = create<CounterStore>()(
  devtools(
    (set) => ({
      count: 0,
      increment: () =>
        set(
          (state) => ({ count: state.count + 1 }),
          undefined,
          'counter/increment', // DevTools中的操作名称
        ),
    }),
    { name: 'CounterStore' }, // DevTools中的Store名称
  ),
)

Combining Multiple Middlewares

组合多个中间件

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

const useStore = create<MyStore>()(
  devtools(
    persist(
      (set) => ({
        // store definition
      }),
      { name: 'my-storage' },
    ),
    { name: 'MyStore' },
  ),
)
Order matters:
devtools(persist(...))
shows persist actions in DevTools.

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

const useStore = create<MyStore>()(
  devtools(
    persist(
      (set) => ({
        // Store定义
      }),
      { name: 'my-storage' },
    ),
    { name: 'MyStore' },
  ),
)
顺序重要
devtools(persist(...))
会在DevTools中显示持久化操作。

Common Patterns

常见模式

Pattern: Computed/Derived Values

模式:计算/派生值

typescript
interface StoreWithComputed {
  items: string[]
  addItem: (item: string) => void
  // Computed in selector, not stored
}

const useStore = create<StoreWithComputed>()((set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}))

// Use in component
function ItemCount() {
  const count = useStore((state) => state.items.length)
  return <div>{count} items</div>
}
typescript
interface StoreWithComputed {
  items: string[]
  addItem: (item: string) => void
  // 计算值在选择器中定义,而非存储
}

const useStore = create<StoreWithComputed>()((set) => ({
  items: [],
  addItem: (item) => set((state) => ({ items: [...state.items, item] })),
}))

// 在组件中使用
function ItemCount() {
  const count = useStore((state) => state.items.length)
  return <div>共有{count}个项目</div>
}

Pattern: Async Actions

模式:异步操作

typescript
interface AsyncStore {
  data: string | null
  isLoading: boolean
  error: string | null
  fetchData: () => Promise<void>
}

const useAsyncStore = create<AsyncStore>()((set) => ({
  data: null,
  isLoading: false,
  error: null,
  fetchData: async () => {
    set({ isLoading: true, error: null })
    try {
      const response = await fetch('/api/data')
      const data = await response.text()
      set({ data, isLoading: false })
    } catch (error) {
      set({ error: (error as Error).message, isLoading: false })
    }
  },
}))
typescript
interface AsyncStore {
  data: string | null
  isLoading: boolean
  error: string | null
  fetchData: () => Promise<void>
}

const useAsyncStore = create<AsyncStore>()((set) => ({
  data: null,
  isLoading: false,
  error: null,
  fetchData: async () => {
    set({ isLoading: true, error: null })
    try {
      const response = await fetch('/api/data')
      const data = await response.text()
      set({ data, isLoading: false })
    } catch (error) {
      set({ error: (error as Error).message, isLoading: false })
    }
  },
}))

Pattern: Resetting Store

模式:重置Store

typescript
interface ResettableStore {
  count: number
  name: string
  increment: () => void
  reset: () => void
}

const initialState = {
  count: 0,
  name: '',
}

const useStore = create<ResettableStore>()((set) => ({
  ...initialState,
  increment: () => set((state) => ({ count: state.count + 1 })),
  reset: () => set(initialState),
}))
typescript
interface ResettableStore {
  count: number
  name: string
  increment: () => void
  reset: () => void
}

const initialState = {
  count: 0,
  name: '',
}

const useStore = create<ResettableStore>()((set) => ({
  ...initialState,
  increment: () => set((state) => ({ count: state.count + 1 })),
  reset: () => set(initialState),
}))

Pattern: Selector with Params

模式:带参数的选择器

typescript
interface TodoStore {
  todos: Array<{ id: string; text: string; done: boolean }>
  addTodo: (text: string) => void
  toggleTodo: (id: string) => void
}

const useStore = create<TodoStore>()((set) => ({
  todos: [],
  addTodo: (text) =>
    set((state) => ({
      todos: [...state.todos, { id: Date.now().toString(), text, done: false }],
    })),
  toggleTodo: (id) =>
    set((state) => ({
      todos: state.todos.map((todo) =>
        todo.id === id ? { ...todo, done: !todo.done } : todo
      ),
    })),
}))

// Use with parameter
function Todo({ id }: { id: string }) {
  const todo = useStore((state) => state.todos.find((t) => t.id === id))
  const toggleTodo = useStore((state) => state.toggleTodo)

  if (!todo) return null

  return (
    <div>
      <input
        type="checkbox"
        checked={todo.done}
        onChange={() => toggleTodo(id)}
      />
      {todo.text}
    </div>
  )
}

typescript
interface TodoStore {
  todos: Array<{ id: string; text: string; done: boolean }>
  addTodo: (text: string) => void
  toggleTodo: (id: string) => void
}

const useStore = create<TodoStore>()((set) => ({
  todos: [],
  addTodo: (text) =>
    set((state) => ({
      todos: [...state.todos, { id: Date.now().toString(), text, done: false }],
    })),
  toggleTodo: (id) =>
    set((state) => ({
      todos: state.todos.map((todo) =>
        todo.id === id ? { ...todo, done: !todo.done } : todo
      ),
    })),
}))

// 带参数使用
function Todo({ id }: { id: string }) {
  const todo = useStore((state) => state.todos.find((t) => t.id === id))
  const toggleTodo = useStore((state) => state.toggleTodo)

  if (!todo) return null

  return (
    <div>
      <input
        type="checkbox"
        checked={todo.done}
        onChange={() => toggleTodo(id)}
      />
      {todo.text}
    </div>
  )
}

Using Bundled Resources

配套资源使用

Templates (templates/)

模板文件(templates/)

This skill includes 8 ready-to-use template files:
  • basic-store.ts
    - Minimal JavaScript store example
  • typescript-store.ts
    - Properly typed TypeScript store
  • persist-store.ts
    - localStorage persistence with migration
  • slices-pattern.ts
    - Modular store organization
  • devtools-store.ts
    - Redux DevTools integration
  • nextjs-store.ts
    - SSR-safe Next.js store with hydration
  • computed-store.ts
    - Derived state patterns
  • async-actions-store.ts
    - Async operations with loading states
Example Usage:
bash
undefined
本方案包含8个可直接使用的模板文件:
  • basic-store.ts
    - 极简JavaScript Store示例
  • typescript-store.ts
    - 类型规范的TypeScript Store
  • persist-store.ts
    - 带迁移的localStorage持久化
  • slices-pattern.ts
    - 模块化Store组织
  • devtools-store.ts
    - Redux DevTools集成
  • nextjs-store.ts
    - SSR安全的Next.js Store(含hydration处理)
  • computed-store.ts
    - 派生状态模式
  • async-actions-store.ts
    - 带加载状态的异步操作
使用示例:
bash
undefined

Copy template to your project

复制模板到项目

cp ~/.claude/skills/zustand-state-management/templates/typescript-store.ts src/store/

**When to use each:**
- Use `basic-store.ts` for quick prototypes
- Use `typescript-store.ts` for most production apps
- Use `persist-store.ts` when state needs to survive page reloads
- Use `slices-pattern.ts` for large, complex stores (100+ lines)
- Use `nextjs-store.ts` for Next.js projects with SSR
cp ~/.claude/skills/zustand-state-management/templates/typescript-store.ts src/store/

**各模板适用场景:**
- 快速原型开发使用`basic-store.ts`
- 大多数生产应用使用`typescript-store.ts`
- 需要状态持久化时使用`persist-store.ts`
- 大型复杂Store(100行以上)使用`slices-pattern.ts`
- Next.js项目使用`nextjs-store.ts`

References (references/)

参考文档(references/)

Deep-dive documentation for complex scenarios:
  • middleware-guide.md
    - Complete middleware documentation (persist, devtools, immer, custom)
  • typescript-patterns.md
    - Advanced TypeScript patterns and troubleshooting
  • nextjs-hydration.md
    - SSR, hydration, and Next.js best practices
  • migration-guide.md
    - Migrating from Redux, Context API, or Zustand v4
When Claude should load these:
  • Load
    middleware-guide.md
    when user asks about persistence, devtools, or custom middleware
  • Load
    typescript-patterns.md
    when encountering complex type inference issues
  • Load
    nextjs-hydration.md
    for Next.js-specific problems
  • Load
    migration-guide.md
    when migrating from other state management solutions
复杂场景的深度参考文档:
  • middleware-guide.md
    - 完整中间件文档(持久化、开发者工具、Immer、自定义中间件)
  • typescript-patterns.md
    - 高级TypeScript模式与故障排除
  • nextjs-hydration.md
    - SSR、hydration及Next.js最佳实践
  • migration-guide.md
    - 从Redux、Context API或Zustand v4迁移指南
何时加载这些文档:
  • 用户询问持久化、开发者工具或自定义中间件时加载
    middleware-guide.md
  • 遇到复杂类型推断问题时加载
    typescript-patterns.md
  • 处理Next.js特定问题时加载
    nextjs-hydration.md
  • 从其他状态管理方案迁移时加载
    migration-guide.md

Scripts (scripts/)

脚本工具(scripts/)

  • check-versions.sh
    - Verify Zustand version and compatibility
Usage:
bash
cd your-project/
~/.claude/skills/zustand-state-management/scripts/check-versions.sh

  • check-versions.sh
    - 验证Zustand版本与兼容性
使用方法:
bash
cd your-project/
~/.claude/skills/zustand-state-management/scripts/check-versions.sh

Advanced Topics

高级主题

Vanilla Store (Without React)

无React的Vanilla Store

typescript
import { createStore } from 'zustand/vanilla'

const store = createStore<CounterStore>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

// Subscribe to changes
const unsubscribe = store.subscribe((state) => {
  console.log('Count changed:', state.count)
})

// Get current state
console.log(store.getState().count)

// Update state
store.getState().increment()

// Cleanup
unsubscribe()
typescript
import { createStore } from 'zustand/vanilla'

const store = createStore<CounterStore>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

// 订阅状态变更
const unsubscribe = store.subscribe((state) => {
  console.log('计数变更:', state.count)
})

// 获取当前状态
console.log(store.getState().count)

// 更新状态
store.getState().increment()

// 清理订阅
unsubscribe()

Custom Middleware

自定义中间件

typescript
import { StateCreator, StoreMutatorIdentifier } from 'zustand'

type Logger = <T>(
  f: StateCreator<T, [], []>,
  name?: string,
) => StateCreator<T, [], []>

const logger: Logger = (f, name) => (set, get, store) => {
  const loggedSet: typeof set = (...a) => {
    set(...(a as Parameters<typeof set>))
    console.log(`[${name}]:`, get())
  }
  return f(loggedSet, get, store)
}

// Use custom middleware
const useStore = create<MyStore>()(
  logger((set) => ({
    // store definition
  }), 'MyStore'),
)
typescript
import { StateCreator, StoreMutatorIdentifier } from 'zustand'

type Logger = <T>(
  f: StateCreator<T, [], []>,
  name?: string,
) => StateCreator<T, [], []>

const logger: Logger = (f, name) => (set, get, store) => {
  const loggedSet: typeof set = (...a) => {
    set(...(a as Parameters<typeof set>))
    console.log(`[${name}]:`, get())
  }
  return f(loggedSet, get, store)
}

// 使用自定义中间件
const useStore = create<MyStore>()(
  logger((set) => ({
    // Store定义
  }), 'MyStore'),
)

Immer Middleware (Mutable Updates)

Immer中间件(可变更新)

typescript
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'

interface TodoStore {
  todos: Array<{ id: string; text: string }>
  addTodo: (text: string) => void
}

const useStore = create<TodoStore>()(
  immer((set) => ({
    todos: [],
    addTodo: (text) =>
      set((state) => {
        // Mutate directly with Immer
        state.todos.push({ id: Date.now().toString(), text })
      }),
  })),
)

typescript
import { create } from 'zustand'
import { immer } from 'zustand/middleware/immer'

interface TodoStore {
  todos: Array<{ id: string; text: string }>
  addTodo: (text: string) => void
}

const useStore = create<TodoStore>()(
  immer((set) => ({
    todos: [],
    addTodo: (text) =>
      set((state) => {
        // 使用Immer直接修改
        state.todos.push({ id: Date.now().toString(), text })
      }),
  })),
)

Dependencies

依赖说明

Required:
  • zustand@5.0.8
    - State management library
  • react@18.0.0+
    - React framework
Optional:
  • @types/node
    - For TypeScript path resolution
  • immer
    - For mutable update syntax
  • Redux DevTools Extension - For devtools middleware

必需依赖
  • zustand@5.0.8
    - 状态管理库
  • react@18.0.0+
    - React框架
可选依赖
  • @types/node
    - TypeScript路径解析
  • immer
    - 可变更新语法支持
  • Redux DevTools扩展 - 开发者工具中间件支持

Official Documentation

官方文档

Package Versions (Verified 2025-10-24)

包版本(2025-10-24验证)

json
{
  "dependencies": {
    "zustand": "^5.0.8",
    "react": "^19.0.0"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "typescript": "^5.0.0"
  }
}
Compatibility:
  • React 18+, React 19 ✅
  • TypeScript 5+ ✅
  • Next.js 14+, Next.js 15+ ✅
  • Vite 5+ ✅

json
{
  "dependencies": {
    "zustand": "^5.0.8",
    "react": "^19.0.0"
  },
  "devDependencies": {
    "@types/node": "^22.0.0",
    "typescript": "^5.0.0"
  }
}
兼容性
  • React 18+、React 19 ✅
  • TypeScript 5+ ✅
  • Next.js 14+、Next.js 15+ ✅
  • Vite 5+ ✅

Troubleshooting

故障排除

Problem: Store updates don't trigger re-renders

问题:Store更新未触发组件重渲染

Solution: Ensure you're using selector functions, not destructuring:
const bears = useStore(state => state.bears)
not
const { bears } = useStore()
解决方案:确保使用选择器函数,而非直接解构:
const bears = useStore(state => state.bears)
而非
const { bears } = useStore()

Problem: TypeScript errors with middleware

问题:TypeScript与中间件配合报错

Solution: Use double parentheses:
create<T>()()
not
create<T>()
解决方案:使用双括号语法:
create<T>()()
而非
create<T>()

Problem: Persist middleware causes hydration error

问题:持久化中间件导致hydration错误

Solution: Implement
_hasHydrated
flag pattern (see Issue #1)
解决方案:实现
_hasHydrated
标志模式(参考问题1)

Problem: Actions not showing in Redux DevTools

问题:操作未显示在Redux DevTools中

Solution: Pass action name as third parameter to
set
:
set(newState, undefined, 'actionName')
解决方案:为
set
方法传递第三个参数作为操作名称:
set(newState, undefined, 'actionName')

Problem: Store state resets unexpectedly

问题:Store状态意外重置

Solution: Check if using HMR (hot module replacement) - Zustand resets on module reload in development

解决方案:检查是否启用了HMR(热模块替换)——开发环境下模块重载时Zustand会重置状态

🔄 Workflow

🔄 工作流程

Aşama 1: Store Definition & Types

步骤1:Store定义与类型

  • Model Selection: State ve Action yapılarını içeren TypeScript interface'lerini belirle.
  • Curry Initialization:
    create<T>()()
    (double parentheses) syntax'ını kullanarak store'u başlat.
  • Middleware Selection: İhtiyaca göre
    persist
    (localStorage) veya
    devtools
    katmanlarını ekle.
  • 模型选择:定义包含State和Action结构的TypeScript接口。
  • 柯里化初始化:使用
    create<T>()()
    (双括号)语法初始化Store。
  • 中间件选择:根据需求添加
    persist
    (localStorage)或
    devtools
    中间件。

Aşama 2: React Integration & Slices

步骤2:React集成与切片

  • Atomic Slices: Büyük store'ları
    StateCreator
    kullanarak atomik dilimlere ayır.
  • Selector Strategy: Bileşenlerin sadece kullandığı state dilimine abone olmasını (
    useStore(state => state.X)
    ) sağla.
  • Shallow Audit: Birden fazla değer seçerken gereksiz render'ları önlemek için
    shallow
    kullan.
  • 原子化切片:使用
    StateCreator
    将大型Store拆分为原子化切片。
  • 选择器策略:确保组件仅订阅其使用的特定状态片段(
    useStore(state => state.X)
    )。
  • Shallow优化:选择多个值时使用
    shallow
    比较避免不必要的重渲染。

Aşama 3: Persistence & Hydration

步骤3:持久化与Hydration

  • Hydration Guard: Next.js projelerinde
    _hasHydrated
    flag pattern'i ile SSR uyumluluğunu sağla.
  • Storage Config: Hassas veriler için
    sessionStorage
    veya özel şifreli storage konfigürasyonunu yap.
  • Action Logging: Hata ayıklama sürecinde aksiyon isimlerini (
    counter/increase
    ) devtools ile izle.
  • Hydration防护:Next.js项目中使用
    _hasHydrated
    标志模式确保SSR兼容性。
  • 存储配置:敏感数据使用
    sessionStorage
    或自定义加密存储配置。
  • 操作日志:调试时为操作命名(如
    counter/increase
    )以便在DevTools中跟踪。

Kontrol Noktaları

检查节点

AşamaDoğrulama
1
set
metodu içerisinde doğrudan mutasyon yapıldı mı? (İllegal!)
2Component içinde selector yerine doğrudan destructuring yapıldı mı? (Performans riski!)
3Persist storage key'i benzersiz (Unique) mi?

Zustand State v2.0 - With Workflow5. GitHub issues: https://github.com/pmndrs/zustand/issues
步骤验证项
1是否在
set
方法中直接修改了状态?(非法操作!)
2组件中是否直接解构Store而非使用选择器?(性能风险!)
3持久化存储键是否唯一?

Zustand State v2.0 - 含工作流程5. GitHub issues: https://github.com/pmndrs/zustand/issues