React动态添加表单输入框点击一次生成两个字段问题排查
React动态表单点击添加按钮一次生成两个输入框修复方案
问题场景
- 技术栈:React + Firestore
- 已实现功能:表单包含数组类型
ingredients字段,支持动态增删输入框,表单提交后数据可正常写入Firebase - 异常表现:点击添加字段按钮时,会同时生成2个输入框,不符合每次点击新增1个的预期
根因分析
异常由两个常见问题叠加导致:
- 状态更新写法不规范:React 18开发环境默认开启
StrictMode严格模式,会对状态更新逻辑做双调用校验,排查不纯的更新逻辑。原有代码中handleIngredientCount、handleIngredient方法直接依赖闭包缓存的form值计算新状态,属于非纯函数更新,在双调用校验下会出现重复追加字段的问题。 - 按钮默认行为冲突:如果添加按钮放在
<form>标签内部且未显式指定type="button",按钮默认type为submit,点击时会同时触发绑定的添加字段逻辑、表单默认提交行为,间接导致逻辑重复执行。
另外原有代码中Firestore实时监听器未设置清理逻辑,会在严格模式下重复注册监听,存在后续列表数据重复渲染的隐患。
修复步骤
1. 所有关联状态更新改用函数式写法
将直接依赖外层闭包状态的更新逻辑,改为通过setter回调获取最新状态的纯函数写法,从根源避免状态更新重复执行、闭包陈旧值问题:
// 修复单条食材输入逻辑 const handleIngredient = (e, i) => { setForm(prevForm => { const ingredientsClone = [...prevForm.ingredients] ingredientsClone[i] = e.target.value return { ...prevForm, ingredients: ingredientsClone } }) } // 修复添加食材字段逻辑 const handleIngredientCount = () => { setForm(prevForm => ({ ...prevForm, ingredients: [...prevForm.ingredients, ""] })) }
2. 显式指定添加按钮的类型
如果添加按钮嵌套在form标签内部,必须给按钮加type="button"属性,阻断默认的表单提交触发逻辑:
{/* 错误示例:<button onClick={handleIngredientCount}>添加食材</button> */} <button type="button" onClick={handleIngredientCount}>添加食材</button>
3. 补全Firestore监听器的清理逻辑
在useEffect中返回onSnapshot的取消订阅方法,避免组件重挂载时重复注册监听器:
useEffect(() => { const unsubscribe = onSnapshot(recipesCollectionRef, snapshot => { setRecipes(snapshot.docs.map(doc => { return { id: doc.id, viewing: false, ...doc.data() } })) }) // 组件卸载时取消订阅 return () => unsubscribe() }, [])
验证结果
修复完成后,每次点击添加按钮只会新增1个食材输入框,表单提交、Firestore数据读写逻辑均正常,开发、生产环境表现一致。
内容的提问来源于stack exchange,提问作者siddhant kumar
相关产品推荐
相关产品推荐

