React zustand todos案例(带本地存储localStorage、persist)todoStore.ts

参考文章: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 路径别名)
    • 复现步骤
      • [步骤 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 store
  • persist:持久化中间件,数据保存到 localStorage(key: todo-storage
  • Todo:任务类型,包含 idtextdoneselected
  • TodoState:store 接口,包含状态和方法
  • set:更新状态,使用函数式更新保证不可变

方法说明:

  • add:添加任务,使用时间戳作为 id
  • toggle:切换完成状态
  • 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 查看效果。

功能演示

  1. 添加任务:点击"添加"按钮,添加一个名为"新任务"的任务
  2. 切换完成状态:点击任务的完成复选框
  3. 选择任务:点击任务的选中复选框
  4. 全选/取消全选:使用顶部按钮
  5. 删除选中:点击"删除"按钮,删除所有选中的任务
  6. 清除已完成:点击"清除已完成"按钮,删除所有已完成的选中任务
  7. 数据持久化:刷新页面后数据仍然保留(存储在 localStorage)

核心概念总结

  1. Zustand:轻量级状态管理,API 简单
  2. 持久化:使用 persist 中间件自动保存到 localStorage
  3. 不可变更新:使用 set 函数式更新,保持状态不可变
  4. TypeScript:类型安全,提供良好的开发体验
  5. 函数式编程:使用 mapfilter 等函数式方法处理数组

扩展建议

  1. 添加输入框:让用户输入自定义任务文本
  2. 编辑功能:支持修改任务内容
  3. 分类功能:为任务添加分类标签
  4. 优先级:添加优先级设置
  5. 日期提醒:添加截止日期功能
  6. UI 优化:使用更美观的 UI 组件库(如 shadcn/ui)

总结

该示例展示了:

  • Zustand 的基本用法
  • 状态持久化
  • TypeScript 类型定义
  • Next.js 客户端组件
  • 函数式状态更新

适合作为学习 Zustand 和状态管理的入门项目。

相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅12 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax