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

React Dialog Modal异常:创建Todo时提前弹出,未按编辑按钮触发

问题修复方案

问题根源

  1. 新创建的Todo项若未初始化isEditing为false,会导致组件挂载时触发弹窗逻辑
  2. 使用document.querySelector('dialog')会选中页面首个dialog,而非当前TodoItem对应的dialog,逻辑错误
  3. 组件挂载时useEffect会执行,若初始条件匹配就会误触发弹窗

修复步骤

1. 确保新Todo项正确初始化

在创建Todo的代码中,必须为新项添加isEditing: false属性(示例如下):

// 示例创建逻辑
const addTodo = (taskText) => {
  setTodos([...todos, { id: Date.now(), task: taskText, description: '', isEditing: false }]);
};

2. 修复TodoItem组件的Dialog获取逻辑

使用React Ref绑定当前组件的Dialog,避免选中错误元素:

import { useEffect, useRef } from "react";

export default function TodoItem({ task, editTask, editingItemId }) {
    const dialogRef = useRef(null);

    useEffect(() => {
        // 仅当当前项处于编辑状态且匹配编辑ID时弹窗
        if (task.id === editingItemId && task.isEditing) {
            dialogRef.current?.showModal();
        }
    }, [task.isEditing, task.id, editingItemId]);

    return (
        <>
            <div className="col-span-4">
                <h2>{task.task}</h2>
                {task.description ? (
                    <p>{task.description}</p>
                ) : (
                    <button
                        onClick={() => editTask(task.id)}
                        className="bg-slate-600 hover:bg-slate-900 text-white font-bold px-2 py-1 m-1 rounded-2xl">
                        Describe your task
                    </button>
                )}
            </div>
            <dialog ref={dialogRef} className="flex flex-col p-12 rounded-lg bg-slate-900 text-white">
                <h2 className="uppercase">{task.task}</h2>
                <textarea cols="60" rows="14" className="bg-slate-900 border border-white" />
            </dialog>
        </>
    );
}

3. 优化TodoList组件的编辑逻辑

用Todo的ID作为key(避免index导致的渲染问题),并优化编辑状态切换逻辑:

import TodoItem from "./TodoItem"
import { useState } from "react"

export default function TodoList( {todos, setTodos} ) {
    const [editingItemId, setEditingItemId] = useState(null)

    function editTask(id) {
        setTodos(prevTodos => prevTodos.map(todo => 
            todo.id === id ? {...todo, isEditing: !todo.isEditing} : todo
        ));
        // 切换编辑ID,避免重复触发
        setEditingItemId(prevId => prevId === id ? null : id);
    }

    return (
        <div className="grid grid-cols-4 gap-4">
            {todos.map((todo) => (
                <TodoItem 
                    key={todo.id}
                    task={todo}
                    editTask={editTask}
                    editingItemId={editingItemId}
                />
            ))}
        </div>
    )
}

可选优化:用React状态直接控制弹窗

完全抛弃DOM操作,用状态控制弹窗显示,逻辑更简洁:

import { useState } from "react";

export default function TodoItem({ task, editTask }) {
    const [isDialogOpen, setIsDialogOpen] = useState(false);

    const handleEditClick = () => {
        editTask(task.id);
        setIsDialogOpen(true);
    };

    return (
        <>
            <div className="col-span-4">
                <h2>{task.task}</h2>
                {task.description ? (
                    <p>{task.description}</p>
                ) : (
                    <button
                        onClick={handleEditClick}
                        className="bg-slate-600 hover:bg-slate-900 text-white font-bold px-2 py-1 m-1 rounded-2xl">
                        Describe your task
                    </button>
                )}
            </div>
            <dialog 
                open={isDialogOpen}
                className="flex flex-col p-12 rounded-lg bg-slate-900 text-white"
                onClose={() => setIsDialogOpen(false)}>
                <h2 className="uppercase">{task.task}</h2>
                <textarea cols="60" rows="14" className="bg-slate-900 border border-white" />
                <button onClick={() => setIsDialogOpen(false)}>关闭</button>
            </dialog>
        </>
    );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 07:53:24