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

如何在React Hook Form中实现动态添加输入元素的需求

React Hook Form 自定义动态字段实现方案

核心用到React Hook Form的原生API即可实现需求,不需要额外安装第三方依赖,具体实现逻辑和示例如下:


核心实现思路

  • 初始化空白表单,不预设任何默认字段
  • 用本地状态存储用户新增的自定义字段名,用于动态渲染输入框
  • 新增字段时调用setValue给表单动态注册字段并赋值初始值
  • 动态渲染输入框时通过register绑定字段,自动同步表单的change、dirty状态

完整可运行示例

import { useForm } from "react-hook-form";
import { useState } from "react";

// 动态表单值类型,字段名不固定
type FormValues = Record<string, any>;

export default function DynamicCustomForm() {
  const { 
    register, 
    handleSubmit, 
    setValue, 
    getValues, 
    unset,
    formState: { dirtyFields, isDirty } 
  } = useForm<FormValues>({
    defaultValues: {} // 初始空白表单
  });

  // 存储已新增的自定义字段名,用于动态渲染
  const [customFields, setCustomFields] = useState<string[]>([]);
  // 外部新增字段的输入框状态
  const [newFieldConfig, setNewFieldConfig] = useState({ fieldName: "", initValue: "" });

  // 处理新增字段提交逻辑
  const handleAddCustomField = () => {
    const { fieldName, initValue } = newFieldConfig;
    // 字段名不能为空,且避免重复添加同名字段
    if (!fieldName.trim() || getValues(fieldName) !== undefined) return;

    // 给表单动态注册新字段并赋值初始值
    setValue(fieldName, initValue, {
      shouldValidate: false,
      shouldDirty: false // 初始赋值默认不算用户修改,如需标记为修改可改为true
    });
    // 更新动态渲染列表
    setCustomFields(prev => [...prev, fieldName]);
    // 清空新增输入框
    setNewFieldConfig({ fieldName: "", initValue: "" });
  };

  // 处理删除动态字段逻辑(可选扩展)
  const handleDeleteField = (fieldName: string) => {
    setCustomFields(prev => prev.filter(item => item !== fieldName));
    unset(fieldName); // 清理表单中对应字段的数据和状态
  };

  // 表单提交逻辑
  const onFormSubmit = (data: FormValues) => {
    console.log("表单全量数据:", data);
    console.log("用户修改过的字段:", dirtyFields);
    console.log("表单是否被修改:", isDirty);
  };

  return (
    <div style={{ padding: "20px", maxWidth: "600px" }}>
      {/* 外部新增字段区域 */}
      <div style={{ marginBottom: "24px", padding: "16px", border: "1px solid #e5e7eb" }}>
        <h4>新增自定义字段</h4>
        <input
          placeholder="请输入字段名"
          value={newFieldConfig.fieldName}
          onChange={(e) => setNewFieldConfig(prev => ({ ...prev, fieldName: e.target.value }))}
          style={{ marginRight: "12px", padding: "4px 8px" }}
        />
        <input
          placeholder="请输入初始值"
          value={newFieldConfig.initValue}
          onChange={(e) => setNewFieldConfig(prev => ({ ...prev, initValue: e.target.value }))}
          style={{ marginRight: "12px", padding: "4px 8px" }}
        />
        <button type="button" onClick={handleAddCustomField} style={{ padding: "4px 12px" }}>
          添加到表单
        </button>
      </div>

      {/* 表单区域 */}
      <form onSubmit={handleSubmit(onFormSubmit)}>
        <h4>表单内容</h4>
        {/* 动态渲染已新增的自定义字段 */}
        {customFields.length === 0 ? (
          <p>暂无自定义字段,请先从上方添加</p>
        ) : (
          customFields.map(fieldName => (
            <div key={fieldName} style={{ marginBottom: "12px", display: "flex", gap: "12px", alignItems: "center" }}>
              <label>{fieldName}:</label>
              <input {...register(fieldName)} style={{ flex: 1, padding: "4px 8px" }} />
              <button type="button" onClick={() => handleDeleteField(fieldName)} style={{ color: "red" }}>
                删除
              </button>
            </div>
          ))
        )}

        {customFields.length > 0 && (
          <button type="submit" style={{ marginTop: "16px", padding: "6px 16px" }}>
            提交表单
          </button>
        )}
      </form>
    </div>
  );
}

关键说明

  • 状态同步:通过register绑定的动态输入框,修改时会自动触发React Hook Form的状态更新,dirtyFields、isDirty、touched等内置状态都会自动同步,不需要额外手动处理
  • 校验扩展:如果需要给动态字段增加校验规则,修改register的入参即可,比如register(fieldName, { required: "该字段为必填项", minLength: { value: 2, message: "最少输入2个字符" } })
  • 状态标记:调用setValue赋值初始值时,可以通过第三个参数调整状态标记,比如设置shouldDirty: true可以让初始赋值就标记为用户修改状态

内容的提问来源于stack exchange,提问作者vipul shinde

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 15:09:03