You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

当前代码存在两个问题:

  1. 新增任务时,从第二个新增项开始会被覆盖,任务数量最多只能到4;
  2. 点击完成任务按钮时,会出现其他任务的completed值变为false的异常。

问题分析与解决

问题1:新增任务重复覆盖、数量受限

原因

  • 组件每次渲染都会重新创建TaskService实例,每个新实例的构造函数都会初始化固定的3个任务,导致之前新增的任务无法持久化。
  • 新增任务的id用this.tasks.length + 1生成,新实例的任务列表初始长度为3,所以每次新增任务的id都是4,React列表的key重复,渲染时后添加的任务会覆盖之前的。

解决方法

  1. 用useRef保存TaskService实例,确保组件生命周期内只创建一次:
    在MyComponentTwo中替换const taskService = new TaskService();为:
    const taskServiceRef = useRef(new TaskService());
    const taskService = taskServiceRef.current;
    
  2. 改用自增计数器生成唯一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的任务数组共享引用,容易引发意外的状态修改。

解决方法

  1. 同样使用useRef保存TaskService实例(和问题1的解决方法一致),确保始终操作同一套任务数据。
  2. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 15:30:55