React Dialog Modal异常:创建Todo时提前弹出,未按编辑按钮触发
问题修复方案
问题根源
- 新创建的Todo项若未初始化
isEditing为false,会导致组件挂载时触发弹窗逻辑 - 使用
document.querySelector('dialog')会选中页面首个dialog,而非当前TodoItem对应的dialog,逻辑错误 - 组件挂载时
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
相关产品推荐
相关产品推荐

