简介
Form-Render 是一个基于 JSON Schema 的动态表单渲染器,支持通过配置化的方式快速生成复杂的表单界面。它提供了丰富的表单组件、灵活的布局系统和强大的验证机制,让开发者能够以声明式的方式构建表单。
核心特性
- JSON Schema 驱动:基于标准的 JSON Schema 规范,支持复杂的数据结构
- 丰富的组件库:内置多种表单组件,支持自定义组件扩展
- 灵活的布局:支持多种布局方式,响应式设计
- 强大的验证:内置验证规则,支持自定义验证逻辑
- 主题定制:支持主题切换和样式定制
- TypeScript 支持:完整的类型定义,提供良好的开发体验
安装
npm install form-render
# 或
yarn add form-render
# 或
pnpm add form-render
基本用法
简单示例
import FormRender, { useForm } from "form-render";
const BasicExample = () => {
const form = useForm();
const schema = {
type: "object",
properties: {
name: {
title: "姓名",
type: "string",
required: true,
placeholder: "请输入姓名",
},
age: {
title: "年龄",
type: "number",
minimum: 0,
maximum: 120,
},
email: {
title: "邮箱",
type: "string",
format: "email",
required: true,
},
},
};
const handleSubmit = (data) => {
console.log("表单数据:", data);
alert("表单提交成功!\n" + JSON.stringify(data, null, 2));
};
const handleReset = () => {
form.resetFields();
};
const handleValidate = async () => {
try {
const valid = await form.validateFields();
console.log("验证结果:", valid);
} catch (error) {
console.log("验证失败:", error);
}
};
const getCurrentFormData = () => {
return form.getFieldsValue();
};
return (
<div className="max-w-2xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-6 text-center text-blue-600">
FormRender 示例
</h1>
<div className="bg-white rounded-lg shadow-lg p-6">
<FormRender
form={form}
schema={schema}
onFinish={handleSubmit}
displayType="row"
labelWidth={120}
/>
<div className="mt-6 flex gap-4 justify-center">
<button
onClick={handleReset}
className="bg-gray-500 hover:bg-gray-600 text-white px-6 py-2 rounded-lg transition-colors duration-200"
>
重置表单
</button>
<button
onClick={handleValidate}
className="bg-green-500 hover:bg-green-600 text-white px-6 py-2 rounded-lg transition-colors duration-200"
>
验证表单
</button>
<button
onClick={() => form.submit()}
className="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded-lg transition-colors duration-200"
>
提交表单
</button>
</div>
</div>
</div>
);
};
export default BasicExample;

复杂表单示例
import FormRender, { useForm } from "form-render";
const ComplexExample = () => {
const form = useForm();
const schema = {
type: "object",
properties: {
// 基础输入
name: {
title: "姓名",
type: "string",
required: true,
placeholder: "请输入姓名",
},
email: {
title: "邮箱",
type: "string",
format: "email",
required: true,
placeholder: "请输入邮箱地址",
},
// 选择类组件
gender: {
title: "性别",
type: "string",
enum: ["male", "female", "other"],
enumNames: ["男", "女", "其他"],
widget: "radio",
required: true,
},
city: {
title: "所在城市",
type: "string",
enum: ["beijing", "shanghai", "guangzhou", "shenzhen"],
enumNames: ["北京", "上海", "广州", "深圳"],
widget: "select",
placeholder: "请选择城市",
},
hobbies: {
title: "兴趣爱好",
type: "array",
items: {
type: "string",
},
enum: ["reading", "music", "sports", "travel"],
enumNames: ["阅读", "音乐", "运动", "旅行"],
widget: "checkboxes",
},
// 日期选择
birthday: {
title: "出生日期",
type: "string",
format: "date",
widget: "datePicker",
},
// 文本域
bio: {
title: "个人简介",
type: "string",
widget: "textarea",
placeholder: "请简单介绍一下自己...",
maxLength: 500,
},
// 开关
newsletter: {
title: "订阅邮件通知",
type: "boolean",
widget: "switch",
default: false,
},
},
};
const handleSubmit = (data) => {
console.log("表单数据:", data);
alert("表单提交成功!\n" + JSON.stringify(data, null, 2));
};
const handleReset = () => {
form.resetFields();
};
const handleValidate = async () => {
try {
const valid = await form.validateFields();
console.log("验证结果:", valid);
} catch (error) {
console.log("验证失败:", error);
}
};
const getCurrentFormData = () => {
return form.getFieldsValue();
};
return (
<div className="max-w-2xl mx-auto p-6">
<h1 className="text-2xl font-bold mb-6 text-center text-blue-600">
FormRender 表单示例
</h1>
<p className="text-gray-600 text-center mb-6">
包含8种常用表单组件:文本输入、邮箱、单选框、下拉选择、多选框、日期选择、文本域、开关
</p>
<div className="bg-white rounded-lg shadow-lg p-6">
<FormRender
form={form}
schema={schema}
onFinish={handleSubmit}
displayType="row"
labelWidth={120}
/>
<div className="mt-6 flex flex-wrap gap-4 justify-center">
<button
onClick={handleReset}
className="bg-gray-500 hover:bg-gray-600 text-white px-6 py-2 rounded-lg transition-colors duration-200"
>
重置表单
</button>
<button
onClick={handleValidate}
className="bg-green-500 hover:bg-green-600 text-white px-6 py-2 rounded-lg transition-colors duration-200"
>
验证表单
</button>
<button
onClick={() => {
const data = getCurrentFormData();
console.log("当前表单数据:", data);
alert("当前表单数据:\n" + JSON.stringify(data, null, 2));
}}
className="bg-yellow-500 hover:bg-yellow-600 text-white px-6 py-2 rounded-lg transition-colors duration-200"
>
提交表单
</button>
</div>
</div>
</div>
);
};
export default ComplexExample;

API 参考
FormRender Props
属性名 | 类型 | 默认值 | 描述 |
---|---|---|---|
form | object | – | 表单实例,通过 useForm() 创建 |
schema | object | – | JSON Schema 配置对象 |
onFinish | function | – | 表单提交成功回调函数 |
onFinishFailed | function | – | 表单提交失败回调函数 |
displayType | string | ‘column’ | 布局方式:’column’ | ‘row’ | ‘inline’ |
labelWidth | number | 110 | 标签宽度(像素) |
maxWidth | number | – | 表单最大宽度 |
widgets | object | {} | 自定义组件映射 |
mapping | object | {} | 字段映射配置 |
readonly | boolean | false | 是否只读模式 |
disabled | boolean | false | 是否禁用表单 |
useForm Hook
useForm 是 form-render 提供的表单管理 Hook,用于创建表单实例。
const form = useForm();
表单实例方法
方法名 | 参数 | 返回值 | 描述 |
---|---|---|---|
getFieldsValue | (nameList?: string[]) | object | 获取表单字段值 |
getFieldValue | (name: string) | any | 获取单个字段值 |
setFieldsValue | (values: object) | void | 设置表单字段值 |
setFieldValue | (name: string, value: any) | void | 设置单个字段值 |
resetFields | (nameList?: string[]) | void | 重置表单字段 |
validateFields | (nameList?: string[]) | Promise | 验证表单字段 |
submit | () | void | 提交表单 |
scrollToField | (name: string) | void | 滚动到指定字段 |
使用示例
const form = useForm();
// 获取所有字段值
const allValues = form.getFieldsValue();
// 获取单个字段值
const name = form.getFieldValue("name");
// 设置字段值
form.setFieldsValue({ name: "张三", age: 25 });
// 重置表单
form.resetFields();
// 验证表单
form
.validateFields()
.then((values) => {
console.log("验证通过:", values);
})
.catch((errors) => {
console.log("验证失败:", errors);
});
// 提交表单
form.submit();
Schema 配置
基础字段类型
// 字符串类型
{
type: 'string',
title: '标题',
placeholder: '占位符',
maxLength: 100,
minLength: 1,
pattern: '^[a-zA-Z]+$', // 正则验证
format: 'email', // 格式验证: 'email' | 'url' | 'date' | 'time'
required: true // 必填字段
}
// 数字类型
{
type: 'number',
title: '数字',
minimum: 0,
maximum: 100,
multipleOf: 5, // 倍数验证
required: true
}
// 布尔类型
{
type: 'boolean',
title: '开关',
widget: 'switch', // 'switch' | 'checkbox'
default: false
}
// 数组类型(多选)
{
type: 'array',
title: '多选列表',
items: {
type: 'string'
},
enum: ['option1', 'option2', 'option3'],
enumNames: ['选项1', '选项2', '选项3'],
widget: 'checkboxes',
minItems: 1,
maxItems: 10
}
// 对象类型
{
type: 'object',
title: '对象',
properties: {
// 子字段定义
}
}
常用 Widget 类型
// 输入框
{ type: 'string', widget: 'input' }
// 文本域
{ type: 'string', widget: 'textarea' }
// 密码框
{ type: 'string', widget: 'password' }
// 下拉选择
{
type: 'string',
widget: 'select',
enum: ['value1', 'value2'],
enumNames: ['显示名1', '显示名2']
}
// 单选框
{
type: 'string',
widget: 'radio',
enum: ['male', 'female'],
enumNames: ['男', '女']
}
// 多选框
{
type: 'array',
widget: 'checkboxes',
items: { type: 'string' },
enum: ['reading', 'music'],
enumNames: ['阅读', '音乐']
}
// 日期选择器
{ type: 'string', format: 'date', widget: 'datePicker' }
// 开关
{ type: 'boolean', widget: 'switch' }
验证规则
内置验证
{
type: 'string',
required: true, // 必填
minLength: 2, // 最小长度
maxLength: 50, // 最大长度
pattern: '^[a-zA-Z]+$', // 正则表达式
format: 'email' // 格式验证
}
自定义验证
import FormRender, { useForm } from "form-render";
const ValidationExample = () => {
const form = useForm();
const schema = {
type: "object",
properties: {
password: {
title: "密码",
type: "string",
widget: "password",
required: true,
minLength: 6,
},
confirmPassword: {
title: "确认密码",
type: "string",
widget: "password",
required: true,
},
},
};
// 自定义验证函数
const validateForm = (formData) => {
const errors = {};
if (formData.password !== formData.confirmPassword) {
errors.confirmPassword = "两次密码输入不一致";
}
if (formData.password && formData.password.length < 6) {
errors.password = "密码长度不能少于6位";
}
return errors;
};
const handleFinish = (data) => {
const errors = validateForm(data);
if (Object.keys(errors).length > 0) {
console.log("验证失败:", errors);
// 可以设置字段错误状态
return;
}
console.log("验证通过,提交数据:", data);
};
return <FormRender form={form} schema={schema} onFinish={handleFinish} />;
};
最佳实践
- 合理使用 Schema 结构:将复杂表单拆分为多个对象层级
- 组件复用:抽取通用的 Schema 配置
- 性能优化:大表单使用分步骤渲染
- 用户体验:提供清晰的验证提示和加载状态
- 类型安全:使用 TypeScript 定义 Schema 类型
常见问题
Q: 如何实现字段联动?
A: 可以通过监听表单值变化来实现字段联动:
const form = useForm();
const [formData, setFormData] = useState({});
// 监听字段变化
const handleValuesChange = (changedValues, allValues) => {
setFormData(allValues);
// 当国家改变时,清空城市选择
if (changedValues.country) {
form.setFieldValue("city", undefined);
}
};
<FormRender form={form} schema={schema} onValuesChange={handleValuesChange} />;
Q: 如何获取和设置表单数据?
A: 使用 form 实例的方法:
const form = useForm();
// 获取所有字段值
const allValues = form.getFieldsValue();
// 获取单个字段值
const name = form.getFieldValue("name");
// 设置字段值
form.setFieldsValue({ name: "张三", age: 25 });
// 设置单个字段值
form.setFieldValue("name", "李四");
Q: 如何自定义表单验证?
A: 在 schema 中定义验证规则,或在 onFinish 中进行自定义验证:
// Schema 中定义
{
type: 'string',
required: true,
minLength: 6,
pattern: '^[a-zA-Z0-9]+$'
}
// 自定义验证逻辑
const handleFinish = (data) => {
if (data.password !== data.confirmPassword) {
// 处理验证失败
return;
}
// 验证通过,提交数据
};
Q: 如何处理文件上传?
A: 使用内置的 upload 组件或自定义上传组件:
{
title: '头像',
type: 'string',
widget: 'upload',
action: '/api/upload',
accept: 'image/*'
}
Q: 如何实现动态表单?
A: 根据条件动态修改 schema:
const [schema, setSchema] = useState(baseSchema);
useEffect(() => {
if (formData.type === "personal") {
setSchema(personalSchema);
} else {
setSchema(companySchema);
}
}, [formData.type]);
Q: 表单性能优化有哪些方法?
A:
- 使用
React.memo
包装自定义组件 - 避免在 render 中创建新的 schema 对象
- 大表单考虑分页或分步骤渲染
- 合理使用
shouldUpdate
控制组件更新
原文链接:https://code.ifrontend.net/archives/904,转载请注明出处。
评论0