news 2026/8/25 22:57:02

react中todolist小案例

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
react中todolist小案例

Todolist.tsx

import React, { useState } from "react"; // 引入 Redux 相关的 Hooks import { useDispatch, useSelector } from "react-redux"; // 引入刚才定义的 Actions,用于触发状态变更 import { addTodo, removeTodo, toggleCompleted } from "../store/todoList"; // 引入 nanoid 用于生成 ID import { nanoid } from "nanoid"; // 引入 RootState 类型,用于类型推断 import type { RootState } from "../store/index"; // 引入 TodoItemType 类型,用于定义本地状态类型 import type { TodoItemType } from "../store/todoList"; /** * TodoList 函数式组件 * 实现了待办事项的增、删、改(状态切换)功能 */ const TodoList = () => { // --- 本地状态 (Component State) --- // 用于管理输入框的受控组件状态 const [inputValue, setInputValue] = useState(""); // --- Redux 状态与方法 --- // 获取 dispatch 函数,用于向 store 发送指令 const dispatch = useDispatch(); // 从 Redux Store 中选取 todos 数据 // 使用泛型 <RootState> 帮助 TypeScript 推断 state 结构 // 使用 `as TodoItemType[]` 进行类型断言(或者确保 RootState 中的 todos 类型定义正确) const todos = useSelector<RootState>((state) => state.todos) as TodoItemType[]; // --- 事件处理函数 --- /** * 处理添加 Todo 的逻辑 */ const handleAddTodo = () => { // 1. 校验输入是否为空 if (inputValue.trim() !== "") { // 2. 构造新的 Todo 对象 const newTodo: TodoItemType = { id: nanoid(5), // 生成短 ID text: inputValue, completed: false, // 默认未完成 }; // 3. 派发 addTodo Action // 注意:这里传入的是整个 Todo 对象,与 Slice 中定义的 PayloadAction<TodoItemType> 对应 dispatch(addTodo(newTodo)); // 4. 清空输入框 setInputValue(""); } }; /** * 处理删除 Todo * @param id - 要删除的 Todo 的 ID */ const handleRemoveTodo = (id: string) => { // 派发 removeTodo Action // 注意:这里传入的是 { id: 'xxx' },与 Slice 中定义的 PayloadAction<{id: string}> 对应 dispatch(removeTodo({ id })); }; /** * 处理切换完成状态 * @param id - 要切换状态的 Todo 的 ID */ const handleToggleCompleted = (id: string) => { dispatch(toggleCompleted({ id })); }; // --- 渲染 JSX --- return ( <> <h2>Todo List</h2> <div> {/* 双向绑定输入框 */} <input type="text" value={inputValue} // 更新本地状态 onChange={(e) => setInputValue(e.target.value)} placeholder="Enter a new todo" /> {/* 绑定添加事件 */} <button onClick={handleAddTodo}>Add</button> </div> <ul> {/* 遍历从 Redux 获取的列表 */} {todos.map((todo) => ( <li key={todo.id}> {/* 复选框绑定 completed 状态 */} <input type="checkbox" checked={todo.completed} // 触发状态切换 onChange={() => handleToggleCompleted(todo.id)} /> {/* 根据 completed 状态动态添加删除线样式 */} <span style={{ textDecoration: todo.completed ? "line-through" : "none", }} > {todo.text} </span> {/* 删除按钮 */} <button onClick={() => handleRemoveTodo(todo.id)}>Delete</button> </li> ))} </ul> </> ); }; export default TodoList;

todoList.ts

// 引入 createSlice 用于创建切片(包含 action 和 reducer) import { createSlice } from "@reduxjs/toolkit"; // 引入 PayloadAction 类型,用于给 reducer 中的 action 定义 payload 的类型 import type { PayloadAction } from "@reduxjs/toolkit"; // 引入 nanoid 用于生成唯一的 ID import { nanoid } from "nanoid"; // --- 类型定义 --- /** * 定义 Todo 项目的类型 */ export type TodoItemType = { id: string; // 唯一标识符 text: string; // 任务描述文本 completed: boolean; // 标记任务是否已完成 }; // --- 初始状态 --- /** * 定义 Todo 列表的初始状态 * 包含两条示例数据 */ const INIT_STATE: TodoItemType[] = [ { id: nanoid(5), text: "learn redux", completed: false, }, { id: nanoid(5), text: "learn typescript", completed: false, }, ]; // --- Slice 创建 --- /** * 创建 TodoList 的 Slice * Slice 会自动生成对应的 action creators 和 reducer 逻辑 */ export const todoListSlice = createSlice({ name: "todoList", // Action type 的前缀,例如:todoList/addTodo initialState: INIT_STATE, // 初始状态数组 reducers: { /** * 添加 Todo 的 Reducer * @param state - Immer 代理的 Draft 状态,可以直接"修改"或返回新状态 * @param action - 包含 payload (新 Todo 对象) 的动作 */ addTodo: (state, action: PayloadAction<TodoItemType>) => { // 方式一:返回新数组 (不可变更新) // return [...state, action.payload]; // 方式二:使用 Immer 推荐的"直接修改"语法 (底层会自动生产新状态) // 这里我们把新任务添加到数组末尾 state.push(action.payload); }, /** * 删除 Todo 的 Reducer * @param action.payload - 一个包含 id 字段的对象,用于指定删除哪个任务 */ removeTodo: (state, action: PayloadAction<{ id: string }>) => { // 解构出要删除的 ID const { id: removeId } = action.payload; // 查找该 ID 对应的索引 const index = state.findIndex((item) => item.id === removeId); // 如果找到了,使用 splice 直接删除 (Immer 允许这种写法) if (index !== -1) { state.splice(index, 1); } }, /** * 切换任务完成状态的 Reducer * @param action.payload - 一个包含 id 字段的对象,用于指定切换哪个任务 */ toggleCompleted: (state, action: PayloadAction<{ id: string }>) => { const { id: toggleId } = action.payload; // 在数组中查找对应的 Todo 项 const todo = state.find((item) => item.id === toggleId); // 如果找到了,直接取反它的 completed 属性 if (todo) { todo.completed = !todo.completed; } }, }, }); // --- 导出 Actions 和 Reducer --- // 导出生成的 Action Creators,用于在组件中 dispatch export const { addTodo, removeTodo, toggleCompleted } = todoListSlice.actions; // 导出 reducer,用于注入到 Redux Store 中 export default todoListSlice.reducer;

app.tsx

import { useState } from 'react' import './App.css' import Count from './pages/Count' import TodoList from './pages/TodoList' function App(){ return ( <> <h1>Redux Demo</h1> <Count /> <TodoList /> </> ) } export default App
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/24 14:47:19

互联网大厂Java小白面试:从Spring Boot到微服务实战技巧

互联网大厂Java小白面试&#xff1a;从Spring Boot到微服务实战技巧 文章简述 本文模拟了一场互联网大厂的Java小白面试&#xff0c;通过场景化的提问与解答&#xff0c;涵盖Spring Boot、微服务、缓存技术等关键技术点&#xff0c;帮助初学者理解技术应用于实际业务场景的方式…

作者头像 李华
网站建设 2026/8/25 1:42:34

PyTorch安装后如何加载Qwen3-14B模型?完整配置教程

PyTorch安装后如何加载Qwen3-14B模型&#xff1f;完整配置教程 在当前AI应用加速落地的背景下&#xff0c;越来越多企业希望将大语言模型部署到本地环境——既要保障数据安全&#xff0c;又要实现快速响应和深度定制。PyTorch作为主流深度学习框架&#xff0c;凭借其灵活的动态…

作者头像 李华
网站建设 2026/8/24 7:10:49

百度网盘高速下载终极指南:告别限速烦恼

还在为百度网盘的"龟速"下载而抓狂吗&#xff1f;每次看到几十KB的下载速度&#xff0c;是不是都想砸键盘&#xff1f;别担心&#xff0c;今天我要分享一个超级实用的解决方案&#xff0c;让你彻底告别限速困扰&#xff0c;享受飞一般的下载体验&#xff01;&#x1…

作者头像 李华
网站建设 2026/8/25 17:49:53

大数据领域数据可视化:助力企业提升决策准确性

大数据领域数据可视化:助力企业提升决策准确性 引言:当大数据遇上“看不懂”的困境 某零售企业的市场总监曾向我抱怨:“我们有TB级的销售数据——每个门店的日销量、每个客户的购买记录、每个产品的库存周转……但这些数据就像一堆乱码,我盯着Excel表格看了3小时,还是不…

作者头像 李华
网站建设 2026/8/24 15:22:56

Flutter Web 与桌面端开发实战:一套代码跑全平台!

一、前言 很多人以为 Flutter 只能做移动端&#xff0c;其实从 Flutter 2.0 起已正式支持 Web 和桌面端&#xff01;本文将带你构建一个“跨五端”应用&#xff08;Android、iOS、Web、Windows、macOS&#xff09;&#xff0c;并解决平台适配的关键问题。 二、启用多平台支持 …

作者头像 李华