当前位置: 首页 > news >正文

HOW - 设计和实现一个动态渲染不同表单类型组件的 DynamicFormItem 组件

目录

  • 背景
  • 方案设计
    • 方案一:通过 `type` 字段判断渲染组件类型
      • 思路
      • 示例结构
      • 优点
      • 缺点
    • 方案二:通过 itemRender 自定义组件渲染
      • 思路
      • 示例结构
      • 优点
      • 缺点
    • 两者对比总结
    • 综合建议

背景

我们已经有了 Select、Input、Radio 等基本表单组件。现在我们对于不同模块,如事件列表、属性列表、用户列表,它们都有一个通用筛选模块。这个筛选模块需要支持配置不同字段(包含 label、type、placeholder 等),对于事件列表,支持事件名称、事件类型、是否上线,对于属性列表,支持属性名称、属性类型…

方案设计

方案一:通过 type 字段判断渲染组件类型

思路

你定义一个统一的渲染组件,如 DynamicFormItem,它根据配置项中传入的 type(如 'input', 'select', 'radio' 等)自动渲染对应的 antd 组件。

示例结构

interface FieldConfig {
  label: string;
  name: string;
  type: 'input' | 'select' | 'radio';
  options?: { label: string; value: string }[];
  placeholder?: string;
  rules?: Rule[];
}

const DynamicFormItem: React.FC<{ field: FieldConfig }> = ({ field }) => {
  const { type, label, name, options = [], ...rest } = field;

  const renderFormItem = () => {
    switch (type) {
      case 'input':
        return <Input placeholder={rest.placeholder} />;
      case 'select':
        return (
          <Select placeholder={rest.placeholder}>
            {options.map(opt => (
              <Select.Option key={opt.value} value={opt.value}>
                {opt.label}
              </Select.Option>
            ))}
          </Select>
        );
      case 'radio':
        return (
          <Radio.Group>
            {options.map(opt => (
              <Radio key={opt.value} value={opt.value}>
                {opt.label}
              </Radio>
            ))}
          </Radio.Group>
        );
      default:
        return null;
    }
  };

  return (
    <Form.Item label={label} name={name} rules={rest.rules}>
      {renderFormItem()}
    </Form.Item>
  );
};

优点

  • 结构清晰,易于配置,适合低代码或表单配置系统。
  • 增加新类型只需扩展 switch 或配置 map。
  • 适用于统一风格、标准化表单。

缺点

  • 灵活性较差,组件渲染逻辑由内部掌控,外部不可定制 UI。
  • 处理复杂交互逻辑(如依赖联动、动态插槽)较为困难。

方案二:通过 itemRender 自定义组件渲染

思路

每个配置项支持传入一个 itemRender 方法,用于返回自定义的表单项组件,这种方式允许你完全控制每一项的渲染逻辑。

示例结构

interface FieldConfig {
  label: string;
  name: string;
  rules?: Rule[];
  itemRender?: () => React.ReactNode;
}

const DynamicFormItem: React.FC<{ field: FieldConfig }> = ({ field }) => {
  const { label, name, rules, itemRender } = field;

  return (
    <Form.Item label={label} name={name} rules={rules}>
      {itemRender ? itemRender() : null}
    </Form.Item>
  );
};

优点

  • 灵活性极高,支持所有 antd 控件、组合控件甚至第三方控件。
  • 更适合高级场景(如复杂的嵌套表单、富文本、自定义行为等)。
  • 渲染逻辑由使用者控制,组件更解耦、可组合性强。

缺点

  • 配置方式更偏向开发者,门槛略高。
  • 所有类型都需要手动写 itemRender,可能重复。

两者对比总结

对比项方案一:基于 type方案二:基于 itemRender
配置复杂度✅ 低(适合配置表单)❌ 高(需要写渲染逻辑)
灵活性❌ 低✅ 高
适用场景表单类型有限、低代码平台个性化表单、交互复杂
可维护性✅ 可统一管理控件逻辑❌ 分散在配置项中
扩展性一定程度可扩展完全可扩展

综合建议

你可以考虑 两者混用的折中方案

interface FieldConfig {
  label: string;
  name: string;
  type?: 'input' | 'select' | 'radio';
  options?: any[];
  itemRender?: () => React.ReactNode;
}

优先用 itemRender,如果没有就按 type 渲染,这样同时具备标准性和灵活性,适合中大型业务系统。

相关文章:

  • SpringBoot框架—Logger使用
  • golang 中 make 和 new 的区别?
  • 力扣刷题——2265.统计值等于子树平均值的节点数
  • 国产三维CAD皇冠CAD在机械制造行业建模教程:油泵
  • [ctfshow web入门] web28
  • 智能仓储数字孪生Demo(Unity实现)
  • AI大模型底层技术——Scaling Law
  • leetcode13.罗马数字转整数
  • 技术面试通关秘籍:常见问题解析+实战案例+避坑指南
  • Java 容器源码分析
  • Java全栈项目--校园快递管理与配送系统(5)
  • Wincc项目被锁定无法打开
  • 红宝书第三十二讲:零基础学会模块打包器:Webpack、Parcel、Rollup
  • 解决.net接口防暴力调用问题
  • 零基础教程:Windows电脑安装Linux系统(双系统/虚拟机)全攻略
  • 特权FPGA之Johnson移位
  • node-modules-inspector 使用以及 node_modules可视化 依赖关联关系快速分析
  • Java 为什么不支持多继承?
  • 多类型医疗自助终端智能化升级路径(代码版.下)
  • C++类与对象进阶知识深度解析
  • 中央宣传部、全国妇联联合发布2025年“最美家庭”
  • 普京批准俄方与乌克兰谈判代表团人员名单
  • “一百零一个愿望——汉字艺术展”亮相意大利威尼斯
  • 黄仕忠丨戏曲文献研究之回顾与展望
  • 外国游客“在华扫货”热:“带空箱子到中国!”
  • 香港根据《维护国家安全条例》订立附属法例