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

React setState异常:点击Save按钮后Edit函数触发,无法退出编辑模式

编辑任务后点击Save按钮无法退出编辑模式的问题

我正在开发一款生产力工具网站,支持用户添加、编辑和删除任务,但遇到了编辑完成后保存按钮的bug。点击Save按钮时,handleSaveTask函数会被调用,但同时handleEditTask函数也被触发,将isEditing状态设回true,导致始终停留在编辑模式。

以下是我的代码:

import React, { useState } from 'react';
import './TaskList.css'

interface taskList {
    id: number;
    name: string;
    start: number;
    end: number;
}

interface TaskListProps {
    tasks: taskList[];
    onChangeTask: Function;
    onDeleteTask: Function;
}

interface TaskProps {
    task: taskList;
    onChange: Function;
    onDelete: Function;
}

export const TaskList: React.FC <TaskListProps> = ({tasks, onChangeTask, onDeleteTask}) => {
    return (
        <div className='center'>
            <h2 className='tasksTitle'>Tasks</h2>
            <div className='taskTitle'>
                <p className='taskTitle1'>Task Name</p>
                <p className='taskTitle2'>Range</p>
            </div>
                {tasks.map((task) => (
                    <Task key={task.id} task={task} onChange={onChangeTask} onDelete={onDeleteTask} />
                ))}
        </div>
    );
}

const Task: React.FC <TaskProps> = ({task, onChange, onDelete}) => {
    const [isEditing, setIsEditing] = useState(false);
    let taskContent;

    const handleEditTask = () => {
        console.log("in Handle Edit Task");
        setIsEditing(true);
    }

    const handleSaveTask = () => {
        console.log("in Handle Save");
        setIsEditing(false);
    }

    if (isEditing) {
        taskContent = (
        <div id='editTask'>
            <input
            value={task.name}
            className="input inputName"
            onChange={(e) => {
                onChange({
                ...task,
                name: e.target.value,
                });
            }}
            />
            <input
            value={task.start}
            className="input inputNumber"
            onChange={(e) => {
                onChange({
                ...task,
                start: e.target.value,
                });
            }}
            />
            <input
            value={task.end}
            className="input inputNumber"
            onChange={(e) => {
                onChange({
                ...task,
                end: e.target.value,
                });
            }}
            />
            <button className="button buttonPrimary addButton" onClick={() => handleSaveTask()}>Save</button>
        </div>
        );
    } else {
        taskContent = (
        <>
            <div className='taskContent'>
                <p className='taskContentItem1'>{task.name}</p>
                <p className='taskContentItem2'>{task.start}-{task.end}</p>
                <div className='taskContentItem3'>
                    <button className='button buttonPrimary' onClick={() => handleEditTask()}>Edit</button>
                    <button className="button buttonSecondary" onClick={() => onDelete(task.id)}>X</button>
                </div>
            </div>
        </>
        );
    }
    return (
        <>
            <div className='center' >
            <label className='taskList'>
            <div className='taskListItem1'>
                {taskContent}
            </div>
            </label>
            </div>
        </>
    );
    }

点击Save按钮后,控制台会同时打印出in Handle Save和in Handle Edit Task。我尝试添加依赖isEditing的useEffect钩子,但引发了无限循环。


原因分析

问题出在你把taskContent放在了<label>标签内部。在HTML中,<label>的点击事件会冒泡传递,且该标签原本用于关联表单控件,此处无关联控件的情况下,点击内部的Save按钮时,事件冒泡到外层<label>后,会间接触发原本隐藏的Edit按钮的点击事件,导致handleEditTask被意外调用,把isEditing重新设为true。

解决方法

直接移除包裹taskContent的<label>标签即可,该标签在此处没有实际作用,反而引发了事件冒泡问题。

修改后的Task组件返回部分代码:

return (
    <>
        <div className='center' >
            <div className='taskListItem1'>
                {taskContent}
            </div>
        </div>
    </>
);

额外优化建议

  1. 给start和end对应的输入框添加type="number"属性,限制输入内容为数字:
<input type="number" value={task.start} className="input inputNumber" ... />
  1. 更新start和end值时,将字符串转为数字,避免类型不匹配:
onChange({
    ...task,
    start: Number(e.target.value),
});

内容的提问来源于stack exchange,提问作者lili4x4

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 03:43:26