ReactJS任务管理组件与TaskService功能异常问题求助
React任务管理组件的问题排查与修复
问题描述
我实现了一个名为MyComponentTwo的任务管理组件,代码如下:
MyComponentTwo.js
import React, { useState, useEffect } from 'react'; import TaskService from '../../services/taskService'; const MyComponentTwo = () => { const [tasks, setTasks] = useState([]); const taskService = new TaskService(); const loadTasks = () => { setTasks(taskService.getAllTasks()); }; useEffect(() => { loadTasks(); }, []); const handleAddTask = () => { const title = prompt('Whats your Task?'); if (title) { taskService.addTask(title); loadTasks(); } }; const handleCompleteTask = (taskId) => { taskService.completeTask(taskId); loadTasks(); }; return ( <div> <h1>My Component Two</h1> <button onClick={handleAddTask}>Add New Task</button> <ul> {tasks.map(task => ( <li key={task.id}> {task.title} - {task.completed ? 'Completed' : 'Pending'} {!task.completed && ( <button onClick={() => handleCompleteTask(task.id)}>Finish</button> )} </li> ))} </ul> </div> ); } export default MyComponentTwo;
taskService.js
class TaskService { constructor() { this.tasks = [ { id: 1, title: 'Buy Bread', completed: false }, { id: 2, title: 'Learn ReactJS', completed: true }, { id: 3, title: 'Do Exercises', completed: false }, ]; } getAllTasks() { return this.tasks; } addTask(title) { const newTask = { id: this.tasks.length + 1, title: title, completed: false }; this.tasks.push(newTask); } completeTask(taskId) { alert(taskId); const task = this.tasks.find(task => task.id === taskId); if (task) { task.completed = true; } } } export default TaskService;
当前代码存在两个问题:
- 新增任务时,从第二个新增项开始会被覆盖,任务数量最多只能到4;
- 点击完成任务按钮时,会出现其他任务的
completed值变为false的异常。
问题分析与解决
问题1:新增任务重复覆盖、数量受限
原因
- 组件每次渲染都会重新创建
TaskService实例,每个新实例的构造函数都会初始化固定的3个任务,导致之前新增的任务无法持久化。 - 新增任务的id用
this.tasks.length + 1生成,新实例的任务列表初始长度为3,所以每次新增任务的id都是4,React列表的key重复,渲染时后添加的任务会覆盖之前的。
解决方法
- 用
useRef保存TaskService实例,确保组件生命周期内只创建一次:
在MyComponentTwo中替换const taskService = new TaskService();为:const taskServiceRef = useRef(new TaskService()); const taskService = taskServiceRef.current; - 改用自增计数器生成唯一id,避免长度变化导致的id重复:
修改taskService.js的构造函数和addTask方法:constructor() { this.tasks = [ { id: 1, title: 'Buy Bread', completed: false }, { id: 2, title: 'Learn ReactJS', completed: true }, { id: 3, title: 'Do Exercises', completed: false }, ]; this.nextId = 4; // 初始化下一个id为4 } addTask(title) { const newTask = { id: this.nextId++, title: title, completed: false }; this.tasks.push(newTask); }
问题2:完成任务时其他任务状态被重置
原因
- 每次渲染创建新的
TaskService实例,调用loadTasks时会把新实例的初始任务数据(部分任务的completed为初始值)覆盖到组件state中,导致之前修改的状态丢失。 getAllTasks直接返回任务数组的引用,组件state和Service的任务数组共享引用,容易引发意外的状态修改。
解决方法
- 同样使用
useRef保存TaskService实例(和问题1的解决方法一致),确保始终操作同一套任务数据。 - 修改
getAllTasks返回数组副本,避免引用共享:getAllTasks() { return [...this.tasks]; // 返回数组副本 }
修改后的完整代码
MyComponentTwo.js
import React, { useState, useEffect, useRef } from 'react'; import TaskService from '../../services/taskService'; const MyComponentTwo = () => { const [tasks, setTasks] = useState([]); // 用useRef保存唯一的TaskService实例 const taskServiceRef = useRef(new TaskService()); const taskService = taskServiceRef.current; const loadTasks = () => { setTasks(taskService.getAllTasks()); }; useEffect(() => { loadTasks(); }, []); const handleAddTask = () => { const title = prompt('Whats your Task?'); if (title) { taskService.addTask(title); loadTasks(); } }; const handleCompleteTask = (taskId) => { taskService.completeTask(taskId); loadTasks(); }; return ( <div> <h1>My Component Two</h1> <button onClick={handleAddTask}>Add New Task</button> <ul> {tasks.map(task => ( <li key={task.id}> {task.title} - {task.completed ? 'Completed' : 'Pending'} {!task.completed && ( <button onClick={() => handleCompleteTask(task.id)}>Finish</button> )} </li> ))} </ul> </div> ); } export default MyComponentTwo;
taskService.js
class TaskService { constructor() { this.tasks = [ { id: 1, title: 'Buy Bread', completed: false }, { id: 2, title: 'Learn ReactJS', completed: true }, { id: 3, title: 'Do Exercises', completed: false }, ]; this.nextId = 4; } getAllTasks() { return [...this.tasks]; } addTask(title) { const newTask = { id: this.nextId++, title: title, completed: false }; this.tasks.push(newTask); } completeTask(taskId) { const task = this.tasks.find(task => task.id === taskId); if (task) { task.completed = true; } } } export default TaskService;
内容的提问来源于stack exchange,提问作者Sudo Sur
相关产品推荐
相关产品推荐

