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

React中同一表格内多表单行的数据存储实现问题

表格多行表单数据按行存入数组的解决方案

核心思路

  • 给每个输入控件(下拉框、文本输入框等)绑定当前行的索引和对应的字段名,触发变更时精准定位要更新的数组元素
  • 遵循React状态不可变原则:不直接修改原数组,通过复制原数组并修改指定位置元素生成新数组,再用状态更新函数替换原状态

具体实现步骤

  1. 编写通用的输入变更处理函数,统一处理所有输入控件的更新逻辑
  2. 完善下拉选择框的更新逻辑,同步修改数组中对应行的level字段
  3. 给所有输入组件绑定索引和字段名参数,确保更新时能定位到目标行和目标字段
  4. 新增动态添加行的功能(可选但实用),满足多行输入需求

修改后的完整代码

const AddEducationDetails: React.FC = () => {
  const [showLevel, setShowLevel] = useState("upto 10");
  const [educations, setEducations] = useState<Education[]>([
    {
      level: "",
      studiedCourse: "",
      studiedAt: "",
      studiedFrom: "",
      passedYear: "",
      board: "",
      university: "",
      file: "",
    },
  ]);

  // 通用输入变更处理:处理文本输入框等控件的更新
  const handleInputChange = (
    e: React.ChangeEvent<HTMLInputElement>,
    index: number,
    field: keyof Education
  ) => {
    const newValue = e.target.value;
    // 不可变更新数组:复制原数组,修改指定索引的目标字段
    setEducations(prev => prev.map((item, i) => 
      i === index ? {...item, [field]: newValue} : item
    ));
  };

  // 处理教育等级下拉选择的更新
  const handleLevelSelection = (
    e: React.ChangeEvent<HTMLSelectElement>,
    index: number,
  ) => {
    const { value } = e.target;
    setShowLevel(value);
    // 同步更新数组中对应行的level字段
    setEducations(prev => prev.map((item, i) => 
      i === index ? {...item, level: value} : item
    ));
  };

  // 新增一行教育经历
  const addNewEducationRow = () => {
    setEducations(prev => [...prev, {
      level: "",
      studiedCourse: "",
      studiedAt: "",
      studiedFrom: "",
      passedYear: "",
      board: "",
      university: "",
      file: "",
    }]);
  };

  return (
    <div>
      <button 
        onClick={addNewEducationRow}
        className="mb-4 px-4 py-2 bg-blue-600 text-white rounded-md"
      >
        添加新的教育经历
      </button>
      <table className="min-w-full overflow-hidden rounded-lg bg-[#373747]">
        {/* 表头示例 */}
        <thead>
          <tr className="border border-gray-600">
            <th className="px-4 py-2 text-gray-200">教育等级</th>
            <th className="px-4 py-2 text-gray-200">所学课程</th>
            <th className="px-4 py-2 text-gray-200">就读学校</th>
            {/* 补充其他表头字段 */}
          </tr>
        </thead>
        <tbody>
          {educations.map((education, index) => (
            <tr key={index} className="border border-gray-600">
              <td className="px-4 py-2">
                <select
                  name="level"
                  value={education.level}
                  onChange={(e) => handleLevelSelection(e, index)}
                  className="mb-4 w-[110px] flex-1 rounded-md border-0 bg-gray-700 p-2 text-gray-200 focus:bg-gray-600 focus:outline-none focus:ring-1 focus:ring-blue-500"
                >
                  <option value="upto 10">Upto 10</option>
                  <option value="+2">+2</option>
                  <option value="above +2">Above +2</option>
                </select>
              </td>
              <td className="px-4 py-2">
                <Input
                  type="text"
                  name="studiedCourse"
                  value={education.studiedCourse}
                  onChange={(e) => handleInputChange(e, index, 'studiedCourse')}
                  className="w-full rounded-md border-0 bg-gray-700 p-2 text-gray-200 focus:bg-gray-600 focus:outline-none focus:ring-1 focus:ring-blue-500"
                />
              </td>
              <td className="px-4 py-2">
                <Input
                  type="text"
                  name="studiedAt"
                  value={education.studiedAt}
                  onChange={(e) => handleInputChange(e, index, 'studiedAt')}
                  className="w-full rounded-md border-0 bg-gray-700 p-2 text-gray-200 focus:bg-gray-600 focus:outline-none focus:ring-1 focus:ring-blue-500"
                />
              </td>
              {/* 其他输入字段可复制上述结构,替换对应的field参数即可 */}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

关键细节说明

  • 不可变更新:使用prev.map(...)生成新数组,避免直接修改原状态,符合React状态更新的核心规则
  • 类型安全:通过keyof Education限定字段参数的类型,避免拼写错误,保证代码健壮性
  • 受控组件:所有输入控件都绑定了对应数组元素的字段值,确保表单内容与状态完全同步

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:54:59