React模态框表单输入值未重置问题排查与解决
问题描述
使用Next.js + Tailwind/Daisy UI开发项目,页面加载JSON数据后渲染源系统表格与关联域表格,点击表格行筛选对应域的功能正常。设置模态框用于新增/编辑源系统:【Edit】按钮传入系统ID,【Create】按钮传入无效ID-1,通过更新selectedSystemID状态控制域表格筛选和模态框数据。
当前问题:
- 页面加载后点击【Create】,模态框显示占位符(因
selectedSystemID=-1,无匹配系统) - 点击【Edit】按钮,模态框正常显示对应系统名称
- 此时再次点击【Create】,尽管
selectedSystemID已更新为-1,筛选函数返回undefined,但模态框输入框仍显示上次编辑的系统名称
问题原因
- 受控组件状态依赖问题:模态框输入框直接绑定
selectedSystem()?.systemName,但输入框作为受控组件,当selectedSystem()返回undefined时,value会被视为undefined,浏览器不会自动清空输入框内容。 - React状态更新异步性:
openModal函数中先调用filterDomains更新selectedSystemID,再打开模态框。由于React状态更新是异步的,组件未完成重渲染时,输入框仍使用之前的缓存值。
修复方案
方案一:给模态框输入框添加独立状态(推荐)
将模态框输入值与selectedSystemID解耦,在打开模态框时初始化输入值,符合React受控组件最佳实践:
import { useEffect, useState } from "react"; export default function Sourcesystem() { const [systems, setSystems] = useState([]); const [selectedSystemID, setSelectedSystemID] = useState(-1); const [modalIsOpen, setModalIsOpen] = useState(false); // 新增模态框输入值的独立状态 const [modalSystemName, setModalSystemName] = useState(""); async function fetchData() { const listSourceSystems = [ { "id": 1, "systemName": "Order Management", "domains": [ { "id": 1, "domainName": "Customer" }, { "id": 2, "domainName": "Order" }, ] }, { "id": 2, "systemName": "Warehouse Management", "domains": [{ "id": 9, "domainName": "Product" }] } ]; setSystems(listSourceSystems); } useEffect(() => { fetchData(); }, []); function filterDomains(systemID) { setSelectedSystemID(systemID); } function selectedSystem() { // 用find替代filter,更高效 return systems.find(s => s.id === selectedSystemID); } function openModal(systemID) { setSelectedSystemID(systemID); // 打开模态框时初始化输入值 const targetSystem = systems.find(s => s.id === systemID); setModalSystemName(targetSystem?.systemName || ""); setModalIsOpen(true); } function closeModal() { setModalIsOpen(false); // 关闭模态框时清空输入值,避免残留 setModalSystemName(""); } return ( <> <div className="flex flex-col mx-10 mt-4"> <h1 className="text-3xl font-bold underline text-center">Source Systems</h1> <div className="divider"></div> <div className="grid h-50 card bg-base-300 rounded-box place-items-center"> <table className="table table-compact w-full"> <thead> <tr> <th className="font-bold px-5">Name</th> <th>actions</th> </tr> </thead> <tbody> {systems.map((system) => ( <tr key={system.id} className={`hover ${system.id === selectedSystemID ? "active text-secondary font-bold" : ""}`} onClick={() => filterDomains(system.id)} > <td className="px-5">{system.systemName}</td> <td> <button className="btn btn-primary btn-sm" onClick={() => openModal(system.id)} > Edit </button> </td> </tr> ))} </tbody> <tfoot> <tr> <td colSpan="4" className="text-center font-bold accent">Add a new Source System</td> <td> <button className="btn btn-primary btn-wide btn-sm" onClick={() => openModal(-1)} > click here </button> </td> </tr> </tfoot> </table> </div> <div className="divider mt-0 before:bg-secondary after:bg-secondary"></div> <div> <div className="grid h-20 card bg-primary-800 rounded-box place-items-center"> <table className="table table-compact w-full table-fixed table-zebra"> <thead> <tr> <th className="text-left px-5">Domain</th> <th className="text-right px-5">Source System</th> </tr> </thead> <tbody> {selectedSystem()?.domains?.map(d => ( <tr key={d.id} className="hover"> <td className="px-5">{d.domainName}</td> <td className="table-cell-2 text-right px-5">{selectedSystem()?.systemName}</td> </tr> ))} </tbody> </table> </div> </div> {/* 模态框修改输入值绑定 */} <input type="checkbox" id="source-system-modal" className="modal-toggle" checked={modalIsOpen} /> <div className="modal"> <div className="modal-box"> <h3>Source System Maintenance</h3> <form> <input type="text" placeholder="System Name placeholder" className="input input-bordered input-primary input-sm w-full" value={modalSystemName} onChange={(e) => setModalSystemName(e.target.value)} /> </form> <div className="modal-action"> <label htmlFor="source-system-modal" className="btn btn-info">Submit</label> <label htmlFor="source-system-modal" className="btn btn-warning btn-outline" onClick={closeModal}>Cancel</label> </div> </div> </div> </div> </> ); }
方案二:优化状态更新与渲染逻辑
若不想新增独立状态,可通过监听selectedSystemID变化,明确处理输入框的undefined情况:
// 组件内添加useEffect监听selectedSystemID变化 useEffect(() => { // 触发组件重渲染,确保输入值更新 }, [selectedSystemID]); // 修改模态框输入框的value属性 <input type="text" placeholder="System Name placeholder" className="input input-bordered input-primary input-sm w-full" value={selectedSystem()?.systemName || ""} />
关键优化点
- 用
find替代filter获取目标系统,遍历更高效 - 方案一中的独立状态完全解耦输入框与筛选逻辑,避免异步状态更新导致的显示延迟
- 关闭模态框时清空输入值,防止下次打开残留历史内容
内容的提问来源于stack exchange,提问作者Aaron Reese
相关产品推荐
相关产品推荐

