Frontend Development 12 min read

Configurable Form Data Design for React/Ant Design Projects

This article introduces a lightweight, configuration‑driven approach to building form fields in React using Ant Design, showing how to replace repetitive FormItem code with a declarative schema, handle edit/detail modes, render different component types, and apply common validation rules.

政采云技术
政采云技术
政采云技术
Configurable Form Data Design for React/Ant Design Projects

In everyday backend and admin system development, forms appear frequently, but traditional implementations involve a lot of repetitive code such as writing FormItem repeatedly, adding generic placeholders, and toggling between edit and detail views with conditional logic. To reduce boilerplate, a configuration‑driven solution is proposed.

General implementation

// 一般实现
import React from 'react';
import { Form, Input, Select } from 'antd';

const Demo = (props) => {
  const { form: { getFieldDecorator }, obj = {}, isEdit } = props;
  return (
    <>
{isEdit ? obj.name || '-' : 
          getFieldDecorator('name', {
            initialValue: obj.name,
          })(
)
        }
{isEdit ? obj.sex || '-' : 
          getFieldDecorator('sex', {
            initialValue: obj.sex,
            rules: [{ required: true, message: '请选择性别' }],
          })(
男
女
)
        }
{isEdit ? obj.phone || '-' : 
          getFieldDecorator('phone', {
            initialValue: obj.phone,
            rules: [{ required: true, message: '请输入手机号' }],
          })(
)
        }
);
};

Configuration‑driven implementation

// 配置化的实现
import React from 'react';
import { renderDataForm } from 'src/util/renderDataForm';

const Demo = (props) => {
  const { form, obj = {}, isEdit } = props;
  const conf = [
    { label: '姓名', field: 'name', initialValue: obj.name, required: false },
    { label: '性别', field: 'sex', initialValue: obj.sex, formItemType: 'Select', options: [{ value: 'male', label: '男' }, { value: 'female', label: '女' }] },
    { label: '手机号', field: 'phone', initialValue: obj.phone },
  ];
  const dataForm = isEdit ? 'form' : 'text';
  return renderDataForm(form, conf, dataForm);
};

Implementation idea

As illustrated in the diagram below, whether the data is displayed as plain text on a detail page or wrapped in a form component on an edit page, it ultimately represents the same underlying data; only the presentation differs. A third form, FormText , is introduced to render text that can still be collected by the form system.

/**
 * 用于 Form 表单内部受控展示文本
 */
export default class FormText extends Component {
  render() {
    const { value, formatMethod = a => a, defaultText = '-', ...resetProps } = this.props;
    return
{formatMethod(value) || defaultText}
;
  }
}

// 使用
{getFieldDecorator('name', { initialValue: 'egg' })(
)}

Specific implementation

1. Form type selection (component or text)

const renderDataForm = (form, conf = {}, dataForm = 'form') => {
  const { customRenderText } = conf;
  return (
{dataForm === 'form' ? renderFormItem(form, conf) :
        customRenderText ? customRenderText(conf) : renderText(conf)}
);
};

2. Rendering the appropriate form component

export const renderFormItem = (form, rest) => {
  const { getFieldDecorator } = form;
  const { label = '', field = '', formItemType = 'input', initialValue, required = true, rules = [], ...itemRest } = rest;
  return getFieldDecorator(field, {
    initialValue,
    rules: [{ required, message: renderMessage(formItemType, label) }, ...rules],
    ...(formItemType === 'upload' ? {
      getValueFromEvent: e => (Array.isArray(e) ? e : e && e.fileList),
      valuePropName: 'fileList',
    } : {}),
  })(renderItem(formItemType, itemRest));
};

const renderItem = (formItemType, itemRest) => {
  const { options = [], CustomFormItem } = itemRest;
  const comps = { Input, TextArea, InputNumber, Upload, Select, RadioGroup, CheckboxGroup, DatePicker };
  if (formItemType === 'CustomFormItem') {
    return
;
  }
  if (!comps[formItemType]) {
    return
;
  }
  const Comp = comps[formItemType];
  if (['Select', 'Upload'].includes(formItemType)) {
    return formItemType === 'Upload' ? (
上传
) : (
{options.map(el => (
{el.label || el.name}
))}
);
  }
  return
;
};

3. Mapping values to display text

export const renderText = (rest) => {
  const { formItemType = 'Input', initialValue, options = [] } = rest;
  switch (formItemType) {
    case 'RadioGroup':
      return (options.find(item => item.value === initialValue) || {}).label || '-';
    case 'DatePick':
      const { format = 'YYYY-MM-DD HH:mm:ss' } = rest;
      return initialValue !== undefined ? moment(initialValue).format(format) : '-';
    default:
      return bizStringFormat(initialValue);
  }
};

4. Common validation rules

export const postCode = /^[0-9]{6}$/;
export const phone = /^1\d{10}$/;
export const postCodeRule = { pattern: postCode, message: '请输入6位数字' };
export const phoneRule = { pattern: phone, message: '请输入11位号码' };

Usage example

const Demo = (props) => {
  const { form } = props;
  const obj = {
    email: '[email protected]',
    addr: '...very long address...',
    sort: 'up',
    birthday: '1999-01-23',
    sex: 'male',
    file: [{ fileId: '123', name: '信用承诺书', size: 1024 }],
  };
  const formConf = [
    { label: '邮箱', field: 'email', initialValue: obj.email, rules: [emailRule] },
    { label: '地址', field: 'addr', initialValue: obj.addr, formItemType: 'TextArea' },
    { label: '排序', field: 'sort', initialValue: obj.sort, formItemType: 'Select', options: [{ value: 'up', label: '升序' }, { value: 'down', label: '降序' }] },
    { label: '生日', field: 'birthday', initialValue: obj.birthday, formItemType: 'DatePicker', format: 'YYYY-MM-DD' },
    { label: '性别', field: 'sex', initialValue: obj.sex, formItemType: 'RadioGroup', options: [{ value: 'male', label: '男' }, { value: 'female', label: '女' }] },
    { label: '信用承诺书', field: 'file', initialValue: obj.file, formItemType: 'Upload' },
  ];
  const dataForm = isEdit ? 'form' : 'text';
  return formConf.map(item => renderDataForm(form, item, dataForm));
};

Conclusion

Although more sophisticated visual configuration solutions exist in the frontend ecosystem, the lightweight approach presented here focuses on a single form block, making the code easier to understand while still providing a configurable, reusable form rendering mechanism.

Two small requests after reading

If you found this article helpful, please:

Click the "Watch" button so more people can see it.

Follow the public account "政采云前端团队" for more curated articles.

We are hiring

The ZooTeam front‑end group in Hangzhou is looking for passionate engineers. We work on material systems, engineering platforms, performance, cloud applications, data analysis, and visualization. If you want to join a fast‑growing team, send your resume to [email protected] .

frontendJavaScriptreactui-componentsAnt DesignForm Configuration
政采云技术
Written by

政采云技术

ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.