天门市文章资讯

React状态管理之Zustand的实现示例

2026-03-29 21:53:01 浏览次数:0
详细信息

1. 基础Store实现

// store/counterStore.ts
import { create } from 'zustand'
import { persist, createJSONStorage } from 'zustand/middleware'

// 定义状态类型
interface CounterState {
  count: number
  name: string
  increment: () => void
  decrement: () => void
  incrementBy: (amount: number) => void
  reset: () => void
  setName: (name: string) => void
}

// 创建store(带持久化)
export const useCounterStore = create<CounterState>()(
  persist(
    (set, get) => ({
      count: 0,
      name: '初始用户',

      increment: () => 
        set((state) => ({ count: state.count + 1 })),

      decrement: () => 
        set((state) => ({ count: state.count - 1 })),

      incrementBy: (amount) => 
        set((state) => ({ count: state.count + amount })),

      reset: () => 
        set({ count: 0 }),

      setName: (name) => 
        set({ name }),
    }),
    {
      name: 'counter-storage', // localStorage的key
      storage: createJSONStorage(() => localStorage),
      partialize: (state) => ({ 
        count: state.count,
        name: state.name 
      }), // 选择性持久化
    }
  )
)

2. 更复杂的Todo示例

// store/todoStore.ts
import { create } from 'zustand'
import { devtools } from 'zustand/middleware'

export interface Todo {
  id: string
  text: string
  completed: boolean
  createdAt: Date
}

interface TodoState {
  todos: Todo[]
  filter: 'all' | 'active' | 'completed'
  addTodo: (text: string) => void
  toggleTodo: (id: string) => void
  deleteTodo: (id: string) => void
  updateTodo: (id: string, text: string) => void
  clearCompleted: () => void
  setFilter: (filter: 'all' | 'active' | 'completed') => void
  getCompletedTodos: () => Todo[]
  getActiveTodos: () => Todo[]
}

export const useTodoStore = create<TodoState>()(
  devtools(
    (set, get) => ({
      todos: [],
      filter: 'all',

      addTodo: (text) =>
        set((state) => ({
          todos: [
            ...state.todos,
            {
              id: Date.now().toString(),
              text,
              completed: false,
              createdAt: new Date(),
            },
          ],
        })),

      toggleTodo: (id) =>
        set((state) => ({
          todos: state.todos.map((todo) =>
            todo.id === id
              ? { ...todo, completed: !todo.completed }
              : todo
          ),
        })),

      deleteTodo: (id) =>
        set((state) => ({
          todos: state.todos.filter((todo) => todo.id !== id),
        })),

      updateTodo: (id, text) =>
        set((state) => ({
          todos: state.todos.map((todo) =>
            todo.id === id ? { ...todo, text } : todo
          ),
        })),

      clearCompleted: () =>
        set((state) => ({
          todos: state.todos.filter((todo) => !todo.completed),
        })),

      setFilter: (filter) => set({ filter }),

      getCompletedTodos: () => 
        get().todos.filter((todo) => todo.completed),

      getActiveTodos: () => 
        get().todos.filter((todo) => !todo.completed),
    }),
    { name: 'TodoStore' }
  )
)

3. 异步操作示例

// store/userStore.ts
import { create } from 'zustand'

interface User {
  id: number
  name: string
  email: string
}

interface UserState {
  user: User | null
  loading: boolean
  error: string | null
  fetchUser: (userId: number) => Promise<void>
  updateUser: (userData: Partial<User>) => Promise<void>
}

export const useUserStore = create<UserState>((set, get) => ({
  user: null,
  loading: false,
  error: null,

  fetchUser: async (userId) => {
    set({ loading: true, error: null })

    try {
      const response = await fetch(
        `https://jsonplaceholder.typicode.com/users/${userId}`
      )

      if (!response.ok) {
        throw new Error('用户获取失败')
      }

      const userData = await response.json()
      set({ user: userData, loading: false })

    } catch (error) {
      set({ 
        error: error instanceof Error ? error.message : '未知错误',
        loading: false 
      })
    }
  },

  updateUser: async (userData) => {
    const { user } = get()
    if (!user) return

    set({ loading: true })

    try {
      // 模拟API请求
      await new Promise(resolve => setTimeout(resolve, 500))

      set((state) => ({
        user: { ...state.user!, ...userData },
        loading: false,
      }))

    } catch (error) {
      set({ 
        error: error instanceof Error ? error.message : '未知错误',
        loading: false 
      })
    }
  },
}))

4. React组件中使用

// components/Counter.tsx
import React from 'react'
import { useCounterStore } from '../store/counterStore'
import { shallow } from 'zustand/shallow'

// 使用selector优化性能(避免不必要的重渲染)
export const Counter = () => {
  const { count, increment, decrement, reset } = useCounterStore(
    (state) => ({
      count: state.count,
      increment: state.increment,
      decrement: state.decrement,
      reset: state.reset,
    }),
    shallow // 浅比较,避免每次创建新对象导致的重复渲染
  )

  return (
    <div>
      <h1>计数: {count}</h1>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
      <button onClick={reset}>重置</button>
    </div>
  )
}
// components/TodoList.tsx
import React, { useState } from 'react'
import { useTodoStore } from '../store/todoStore'

export const TodoList = () => {
  const [input, setInput] = useState('')
  const {
    todos,
    filter,
    addTodo,
    toggleTodo,
    deleteTodo,
    clearCompleted,
    setFilter,
    getActiveTodos,
    getCompletedTodos,
  } = useTodoStore()

  const displayTodos = filter === 'all' 
    ? todos 
    : filter === 'active' 
      ? getActiveTodos()
      : getCompletedTodos()

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    if (input.trim()) {
      addTodo(input.trim())
      setInput('')
    }
  }

  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="添加新任务..."
        />
        <button type="submit">添加</button>
      </form>

      <div>
        <button onClick={() => setFilter('all')}>全部</button>
        <button onClick={() => setFilter('active')}>未完成</button>
        <button onClick={() => setFilter('completed')}>已完成</button>
      </div>

      <ul>
        {displayTodos.map((todo) => (
          <li key={todo.id}>
            <input
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
            />
            <span style={{
              textDecoration: todo.completed ? 'line-through' : 'none'
            }}>
              {todo.text}
            </span>
            <button onClick={() => deleteTodo(todo.id)}>删除</button>
          </li>
        ))}
      </ul>

      <button onClick={clearCompleted}>
        清除已完成 ({getCompletedTodos().length})
      </button>
    </div>
  )
}

5. 跨Store通信

// store/index.ts
import { create } from 'zustand'
import { counterStore } from './counterStore'
import { todoStore } from './todoStore'

// 组合多个store
const useCombinedStore = create((set, get) => ({
  ...counterStore(set, get),
  ...todoStore(set, get),
}))

6. TypeScript类型安全配置

// store/types.ts
// 集中管理类型定义
export type { CounterState } from './counterStore'
export type { Todo, TodoState } from './todoStore'
export type { User, UserState } from './userStore'

7. 测试示例

// __tests__/counterStore.test.ts
import { act, renderHook } from '@testing-library/react'
import { useCounterStore } from '../store/counterStore'

describe('CounterStore', () => {
  beforeEach(() => {
    const { result } = renderHook(() => useCounterStore())
    act(() => {
      result.current.reset()
    })
  })

  it('should increment counter', () => {
    const { result } = renderHook(() => useCounterStore())

    act(() => {
      result.current.increment()
    })

    expect(result.current.count).toBe(1)
  })

  it('should decrement counter', () => {
    const { result } = renderHook(() => useCounterStore())

    act(() => {
      result.current.increment()
      result.current.decrement()
    })

    expect(result.current.count).toBe(0)
  })
})

主要优点

简洁直观:API设计简单,学习成本低 类型安全:完整的TypeScript支持 性能优化:自动去重渲染,支持selector模式 中间件支持:持久化、DevTools、日志等 无样板代码:不需要Provider包裹 灵活的更新:支持函数式和对象式更新

最佳实践

将store拆分为逻辑模块 使用selector优化性能 合理使用中间件 类型定义集中管理 异步操作统一错误处理 使用DevTools进行调试

这个示例展示了Zustand的核心功能,你可以根据实际需求进行调整和扩展。

相关推荐