React zustand todos案例(带本地存储localStorage、persist)todoStore.ts
参考文章:React zustand教程(create函数、persist中间件、zustand/middleware、Redux DevTools)查看本地存储localStorage、immer
文章目录
- 使用 Zustand 构建 Todo 应用完整教程
- 项目简介
- 技术栈
- 项目结构
- 核心代码解析
- 1. Zustand Store 定义 (`src/store/todoStore.ts`)
- 2. 页面组件 (`app/page.tsx`)
- 3. 配置文件
- `package.json` 关键依赖
- `tsconfig.json` 路径别名
- 复现步骤
- 步骤 1:创建 Next.js 项目
- 步骤 2:安装依赖
- 步骤 3:创建 Store 文件
- 步骤 4:创建页面组件
- 步骤 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)
// 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) => voidtoggle: (id: string) => voidtoggleSelect: (id: string) => voidselectAll: () => voiddeselectAll: () => voidremove: (id?: string) => voidclearDone: () => 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)
// 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 关键依赖
"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 路径别名
"paths": {"@/*": ["./*"]}
复现步骤
步骤 1:创建 Next.js 项目
npx create-next-app@latest my-todo-app
# 选择 TypeScript、Tailwind CSS、App Router
步骤 2:安装依赖
npm install zustand
步骤 3:创建 Store 文件
创建 src/store/todoStore.ts,复制上面的 store 代码。
步骤 4:创建页面组件
修改 app/page.tsx,复制上面的页面组件代码。
步骤 5:运行项目
npm run dev
访问 http://localhost:3000 查看效果。
功能演示
- 添加任务:点击“添加”按钮,添加一个名为“新任务”的任务
- 切换完成状态:点击任务的完成复选框
- 选择任务:点击任务的选中复选框
- 全选/取消全选:使用顶部按钮
- 删除选中:点击“删除”按钮,删除所有选中的任务
- 清除已完成:点击“清除已完成”按钮,删除所有已完成的选中任务
- 数据持久化:刷新页面后数据仍然保留(存储在 localStorage)
核心概念总结
- Zustand:轻量级状态管理,API 简单
- 持久化:使用
persist中间件自动保存到 localStorage - 不可变更新:使用
set函数式更新,保持状态不可变 - TypeScript:类型安全,提供良好的开发体验
- 函数式编程:使用
map、filter等函数式方法处理数组
扩展建议
- 添加输入框:让用户输入自定义任务文本
- 编辑功能:支持修改任务内容
- 分类功能:为任务添加分类标签
- 优先级:添加优先级设置
- 日期提醒:添加截止日期功能
- UI 优化:使用更美观的 UI 组件库(如 shadcn/ui)
总结
该示例展示了:
- Zustand 的基本用法
- 状态持久化
- TypeScript 类型定义
- Next.js 客户端组件
- 函数式状态更新
适合作为学习 Zustand 和状态管理的入门项目。
