手写Antd的form组件源码
Ant Design 的表单组件 Form
是一个功能丰富的表单管理工具,它提供了表单的创建、验证、提交等核心功能。以下是其源码的详细解析和完整代码结构。
1. 源码位置
- 官方仓库地址:https://github.com/ant-design/ant-design
Form
组件路径:- 主要文件:
components/form/Form.tsx
- 表单项:
components/form/FormItem.tsx
- 表单上下文:
components/form/context.tsx
- 表单 Hook:
components/form/useForm.ts
- 主要文件:
2. 核心模块结构
2.1 入口文件:components/form/index.tsx
import React from 'react';
import Form from './Form';
export default Form;
这是对外暴露的入口点。
2.2 Form
组件定义(components/form/Form.tsx
)
import React, { forwardRef } from 'react';
import useSubscribe from './useSubscribe';import useForm from './useForm';
import FormContext from './context';import FormItem from './FormItem';
const InternalForm = forwardRef((props, ref) => {const [form] = useForm();
const { children } = props;
return (
<FormContext.Provider value={form}>
<form {...props}>{children}</form>
</FormContext.Provider> );});
const Form = InternalForm;
Form.Item = FormItem;
export default Form;
功能说明:
- 使用
forwardRef
支持ref
转发。 - 使用
useForm
创建表单实例。 - 使用
FormContext.Provider
提供全局表单状态。 Form.Item
是子组件,用于包裹输入控件(如Input
,Select
等)。
2.2.1. Form.Item = FormItem;
Form.Item = FormItem;
是一种在 React 组件中为组件添加静态属性的写法,用于将 FormItem
作为 Form
组件的一个子组件暴露出去。这种写法常见于 Ant Design 这类 UI 框架中,使得开发者可以通过 Form.Item
的方式来使用表单项组件。
语法解释
Form.Item = FormItem;
这行代码的意思是:
- 将
FormItem
组件赋值给Form.Item
,使其成为Form
的一个“静态属性”。 - 这样你就可以在 JSX 中像这样使用它:
<Form> <Form.Item label="用户名" name="username"> <Input /> </Form.Item> </Form>
为什么这么写?
-
命名空间管理
- 把相关组件组织在一起,形成一种“父子结构”的命名空间,便于管理和使用。
- 类似于
Table.Column
、Tabs.TabPane
等写法。
-
提高可读性和一致性
- 使用
Form.Item
而不是单独引入FormItem
,可以增强组件之间的语义关联。 - 在大型项目或 UI 库中保持统一风格。
- 使用
-
避免额外导入
- 不需要再写:
import FormItem from 'antd/lib/form/FormItem';
- 而只需从
Form
上访问即可。
- 不需要再写:
示例:
// Form.tsx
const Form = forwardRef((props, ref) => {return <form>{props.children}</form>;
});Form.Item = FormItem;export default Form;
// FormItem.tsx
const FormItem = ({ label, name, children }) => {// 实现字段绑定、验证等逻辑return (<div className="form-item"><label>{label}</label>{children}</div>);
};export default FormItem;
完整结构 错误示例(不推荐)
如果你不用这种方式而是这样写:
import FormItem from './FormItem';
// 使用时
<FormItem label="用户名" name="username">...</FormItem>
虽然也能工作,但会破坏组件的层级结构和封装性,不利于维护和 API 设计
2.3 表单项组件:components/form/FormItem.tsx
import React, { useEffect } from 'react';import useField from './useField';
import FormContext from './context';
const FormItem = ({ name, rules = [], children }) => {
const form = React.useContext(FormContext);
const field = useField(name, { rules });
useEffect(() => {
form.registerField(field);
}, []);
return React.cloneElement(children, {
value: field.value,
onChange: (e) => field.onChange(e.target.value),
});
};
export default FormItem;
功能说明:
- 使用
React.cloneElement
将字段值和变更事件绑定到子组件。 - 使用
useField
管理字段的状态和验证规则。 - 注册字段到表单实例中。
2.4 表单 Hook:components/form/useForm.ts
import React from 'react';
import FormStore from './store';
function useForm() {
const store = React.useMemo(() => new FormStore(), []);return [store];
}
export default useForm
useForm;
功能说明:
- 创建并返回一个
FormStore
实例。 - 使用
useMemo
避免重复创建实例。
2.5 表单存储类:components/form/store.js
class FormStore {constructor() {this.fields = {};this.callbacks = {};}registerField(field) {this.fields[field.name] = field;}getFieldValue(name) {return this.fields[name]?.value;}setFieldsValue(values) {Object.keys(values).forEach(name => {if (this.fields[name]) {this.fields[name].setValue(values[name]);}});}validateFields() {const errors = {};Object.values(this.fields).forEach(field => {const error = field.validate();if (error) {errors[field.name] = error;}});return Object.keys(errors).length ? errors : null;}submit() {const errors = this.validateFields();if (!errors) {const values = Object.entries(this.fields).reduce((acc, [name, field]) => {acc[name] = field.value;return acc;}, {});this.callbacks.onFinish?.(values);} else {this.callbacks.onFinishFailed?.({ values: {}, errorFields: errors });}}setCallbacks(callbacks) {this.callbacks = callbacks;}
}export default FormStore;
功能说明:
registerField
: 注册字段。getFieldValue
: 获取字段值。setFieldsValue
: 设置字段值。validateFields
: 触发表单验证。submit
: 提交表单。setCallbacks
: 设置回调函数(如onFinish
,onFinishFailed
)。
2.6 字段状态管理的核心 Hook components/form/useField.ts
import React from 'react';
import warning from 'warning';type Store = Record<string, any>;
type Rule = { required?: boolean; message?: string };interface FieldConfig {name: string;rules?: Rule[];
}function useField(name: string, config: FieldConfig) {const [value, setValue] = React.useState('');const [error, setError] = React.useState('');// 注册到 Form 实例中const form = React.useContext(FormContext);React.useEffect(() => {form.registerField({ name, validate });}, []);// 设置值const handleChange = (e) => {const newValue = e.target.value;setValue(newValue);form.setFieldValue(name, newValue);};// 验证规则const validate = () => {const { rules = [] } = config;for (const rule of rules) {if (rule.required && !value) {setError(rule.message || `${name} is required`);return rule.message || `${name} is required`;}}setError('');return null;};return {value,error,onChange: handleChange,validate,};
}
2.7 表单上下文:components/form/context.js
import React from 'react';const FormContext = React.createContext(null);
export default FormContext;
提供全局表单实例,供 FormItem
使用。
3. 完整使用示例
import React from 'react';
import Form from './Form';
import FormItem from './FormItem';
import Input from '../input/Input';const DemoForm = () => {const [form] = Form.useForm();const onFinish = (values) => {console.log('Success:', values);};const onFinishFailed = (errorInfo) => {console.log('Failed:', errorInfo);};return (<Form form={form} onFinish={onFinish} onFinishFailed={onFinishFailed}><FormItem label="用户名" name="username" rules={[{ required: true }]}><Input /></FormItem><FormItem label="密码" name="password" rules={[{ required: true }]}><Input.Password /></FormItem><Button type="primary" htmlType="submit">提交</Button></Form>);
};
4. 总结
模块 | 功能 |
---|---|
Form | 表单容器,管理整体状态 |
FormItem | 表单项,管理单个字段的状态和验证 |
useForm | 创建和管理表单实例 |
FormStore | 表单数据存储 |
useField | 管理字段的状态和验证 |
FormContext | 提供全局表单配置 |
如果你希望进一步了解某个具体模块(如 useField
或 FormStore
的实现细节),可以继续深入查看 Ant Design 的源码仓库或官方文档。