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.
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] .
政采云技术
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.