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

React动态添加表单输入框点击一次生成两个字段问题排查

React动态表单点击添加按钮一次生成两个输入框修复方案

问题场景

  • 技术栈:React + Firestore
  • 已实现功能:表单包含数组类型ingredients字段,支持动态增删输入框,表单提交后数据可正常写入Firebase
  • 异常表现:点击添加字段按钮时,会同时生成2个输入框,不符合每次点击新增1个的预期

根因分析

异常由两个常见问题叠加导致:

  1. 状态更新写法不规范:React 18开发环境默认开启StrictMode严格模式,会对状态更新逻辑做双调用校验,排查不纯的更新逻辑。原有代码中handleIngredientCount、handleIngredient方法直接依赖闭包缓存的form值计算新状态,属于非纯函数更新,在双调用校验下会出现重复追加字段的问题。
  2. 按钮默认行为冲突:如果添加按钮放在<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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 22:24:17