参考文章:React zustand教程(create函数、persist中间件、zustand/middleware、Redux DevTools)查看本地存储localStorage、immer
文章目录
- [使用 Zustand 构建 Todo 应用完整教程](#使用 Zustand 构建 Todo 应用完整教程)
-
- 项目简介
- 技术栈
- 项目结构
- 核心代码解析
-
- [1. Zustand Store 定义 (`src/store/todoStore.ts`)](#1. Zustand Store 定义 (
src/store/todoStore.ts)) - [2. 页面组件 (`app/page.tsx`)](#2. 页面组件 (
app/page.tsx)) - [3. 配置文件](#3. 配置文件)
-
- [`package.json` 关键依赖](#
package.json关键依赖) - [`tsconfig.json` 路径别名](#
tsconfig.json路径别名)
- [`package.json` 关键依赖](#
- [1. Zustand Store 定义 (`src/store/todoStore.ts`)](#1. Zustand Store 定义 (
- 复现步骤
-
- [步骤 1:创建 Next.js 项目](#步骤 1:创建 Next.js 项目)
- [步骤 2:安装依赖](#步骤 2:安装依赖)
- [步骤 3:创建 Store 文件](#步骤 3:创建 Store 文件)
- [步骤 4:创建页面组件](#步骤 4:创建页面组件)
- [步骤 5:运行项目](#步骤 5:运行项目)
- 功能演示
- 核心概念总结
- 扩展建议
- 总结
使用 Zustand 构建 Todo 应用完整教程
项目简介
基于 Next.js 15 和 Zustand 的 Todo 应用,支持:
- 添加任务
- 切换完成状态
- 批量选择/取消选择
- 删除选中任务
- 清除已完成的选中任务
- 数据持久化(localStorage)

技术栈
- Next.js 15.5.4
- React 19.1.0
- Zustand 5.0.8(状态管理)
- TypeScript 5
- Tailwind CSS 4.1.14
项目结构
my-project/
├── app/
│ ├── page.tsx # 主页面组件
│ ├── layout.tsx # 根布局
│ └── globals.css # 全局样式
├── src/
│ └── store/
│ └── todoStore.ts # Zustand 状态管理
├── package.json
└── tsconfig.json
核心代码解析
1. Zustand Store 定义 (src/store/todoStore.ts)
ts
// src/store/todoStore.ts
import { create } from 'zustand'
import { persist } from 'zustand/middleware'
type Todo = { id: string; text: string; done: boolean; selected?: boolean }
interface TodoState {
todos: Todo[]
add: (text: string) => void
toggle: (id: string) => void
toggleSelect: (id: string) => void
selectAll: () => void
deselectAll: () => void
remove: (id?: string) => void
clearDone: () => void
}
export const useTodoStore = create<TodoState>()(
persist(
(set) => ({
todos: [],
add: (text) =>
set((state) => ({
todos: [...state.todos, { id: Date.now().toString(), text, done: false, selected: false }],
})),
// 切换完成状态,如果id相同,则done状态取反,否则不变
toggle: (id) =>
set((state) => ({
todos: state.todos.map(t => t.id === id ? { ...t, done: !t.done } : t),
})),
// 切换选中状态
toggleSelect: (id) =>
set((state) => ({
todos: state.todos.map(t => t.id === id ? { ...t, selected: !t.selected } : t),
})),
// 全选
selectAll: () =>
set((state) => ({
todos: state.todos.map(t => ({ ...t, selected: true })),
})),
// 取消全选
deselectAll: () =>
set((state) => ({
todos: state.todos.map(t => ({ ...t, selected: false })),
})),
// 删除:如果传入id则删除该id,否则删除所有选中的项
remove: (id) => {
if (id) {
set((state) => ({ todos: state.todos.filter(t => t.id !== id) }));
} else {
set((state) => ({ todos: state.todos.filter(t => !t.selected) }));
}
},
// 清除已完成:删除所有选中的已完成项
clearDone: () => set((state) => ({
todos: state.todos.filter(t => !(t.selected && t.done))
})),
}),
{ name: 'todo-storage' }
)
)
代码解析:
create:创建 Zustand storepersist:持久化中间件,数据保存到 localStorage(key:todo-storage)Todo:任务类型,包含id、text、done、selectedTodoState:store 接口,包含状态和方法set:更新状态,使用函数式更新保证不可变
方法说明:
add:添加任务,使用时间戳作为 idtoggle:切换完成状态toggleSelect:切换选中状态selectAll/deselectAll:批量选择/取消remove:删除单个(传 id)或批量删除选中项(不传 id)clearDone:删除已完成的选中项
2. 页面组件 (app/page.tsx)
ts
// page.tsx
'use client'
import { useTodoStore } from '@/src/store/todoStore'
export default function ProfilePage() {
const { todos, add, toggle, remove, clearDone, selectAll, deselectAll, toggleSelect } = useTodoStore()
return (
<div className="p-8">
<div className="flex gap-2">
<button onClick={() => add('新任务')}>添加</button>
<button onClick={() => selectAll()}>全选</button>
<button onClick={() => deselectAll()}>取消全选</button>
<button onClick={() => remove()}>删除</button>
<button onClick={clearDone}>清除已完成</button>
</div>
<ul>
{todos.map(todo => (
<li key={todo.id} className="flex gap-2">
<span>id: {todo.id}</span>
<span>text: {todo.text}</span>
<span>done: {todo.done ? '已完成' : '未完成'}</span>
<input type="checkbox" checked={todo.done} onChange={() => toggle(todo.id)} />
<span>{todo.selected ? '选中' : '未选中'}</span>
<input type="checkbox" checked={todo.selected} onChange={() => toggleSelect(todo.id)} />
</li>
))}
</ul>
</div>
)
}
代码解析:
'use client':Next.js 客户端组件useTodoStore():获取 store 状态和方法- 按钮:触发对应操作
- 列表:渲染任务,每个任务显示 id、文本、完成状态、完成复选框、选中状态、选中复选框
3. 配置文件
package.json 关键依赖
json
"dependencies": {
"@radix-ui/react-slot": "^1.2.3",
"@tailwindcss/postcss": "^4.1.14",
"@tanstack/react-query": "^5.90.5",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.545.0",
"next": "15.5.4",
"postcss": "^8.5.6",
"react": "19.1.0",
"react-dom": "19.1.0",
"react-error-boundary": "^6.0.0",
"react-hot-toast": "^2.6.0",
"react-router-dom": "^7.9.4",
"tailwind-merge": "^3.3.1",
"tailwindcss": "^4.1.14",
"zustand": "^5.0.8"
},
tsconfig.json 路径别名
json
"paths": {
"@/*": ["./*"]
}
复现步骤
步骤 1:创建 Next.js 项目
bash
npx create-next-app@latest my-todo-app
# 选择 TypeScript、Tailwind CSS、App Router
步骤 2:安装依赖
bash
npm install zustand
步骤 3:创建 Store 文件
创建 src/store/todoStore.ts,复制上面的 store 代码。
步骤 4:创建页面组件
修改 app/page.tsx,复制上面的页面组件代码。
步骤 5:运行项目
bash
npm run dev
访问 http://localhost:3000 查看效果。
功能演示
- 添加任务:点击"添加"按钮,添加一个名为"新任务"的任务
- 切换完成状态:点击任务的完成复选框
- 选择任务:点击任务的选中复选框
- 全选/取消全选:使用顶部按钮
- 删除选中:点击"删除"按钮,删除所有选中的任务
- 清除已完成:点击"清除已完成"按钮,删除所有已完成的选中任务
- 数据持久化:刷新页面后数据仍然保留(存储在 localStorage)
核心概念总结
- Zustand:轻量级状态管理,API 简单
- 持久化:使用
persist中间件自动保存到 localStorage - 不可变更新:使用
set函数式更新,保持状态不可变 - TypeScript:类型安全,提供良好的开发体验
- 函数式编程:使用
map、filter等函数式方法处理数组
扩展建议
- 添加输入框:让用户输入自定义任务文本
- 编辑功能:支持修改任务内容
- 分类功能:为任务添加分类标签
- 优先级:添加优先级设置
- 日期提醒:添加截止日期功能
- UI 优化:使用更美观的 UI 组件库(如 shadcn/ui)
总结
该示例展示了:
- Zustand 的基本用法
- 状态持久化
- TypeScript 类型定义
- Next.js 客户端组件
- 函数式状态更新
适合作为学习 Zustand 和状态管理的入门项目。