// 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
}), // 选择性持久化
}
)
)
// 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' }
)
)
// 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
})
}
},
}))
// 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>
)
}
// 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),
}))
// store/types.ts
// 集中管理类型定义
export type { CounterState } from './counterStore'
export type { Todo, TodoState } from './todoStore'
export type { User, UserState } from './userStore'
// __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)
})
})
这个示例展示了Zustand的核心功能,你可以根据实际需求进行调整和扩展。