# react-form-ts **Repository Path**: songyipantest/react-form-ts ## Basic Information - **Project Name**: react-form-ts - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-02 - **Last Updated**: 2025-09-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # React 表单组件深度解析 > 从零实现一个类似 Ant Design 的表单系统 ## 项目概述 这是一个仿照 Ant Design 设计的 React 表单组件,核心功能包括: - 实时验证与批量验证 - 统一的状态管理 - 灵活的组件属性注入 - 完整的 TypeScript 支持 ## 架构设计 ### 文件结构 ``` myForm/ ├── form.tsx # 表单容器组件 ├── item.tsx # 表单项组件 ├── formContext.tsx # 状态共享Context └── index.ts # 统一导出 ``` ### 组件关系 ``` App.tsx └── MyForm (form.tsx) ├── FormContext.Provider └── MyForm.Item (item.tsx) └── 子组件 (input/checkbox等) ``` ## 核心代码详解 ### 1. FormContext - 状态管理中心 **作用:** 在 Form 和 Item 组件间共享数据和方法 ```typescript // formContext.tsx interface FormContextProps { onValueChange?: (key: string, value: any) => void; // 字段值变更回调 validateRegister?: (name: string, cb: Function) => void; // 验证器注册方法 values?: Record; // 表单数据对象 setValues?: (values: Record) => void; // 批量设置表单数据 } export const FormContext = createContext({}); ``` **设计要点:** - 使用 Context 避免 props 层层传递 - 提供数据读写和验证注册的统一接口 ### 2. Form 组件 - 表单容器 **核心职责:** 状态管理、验证器收集、表单提交处理 #### 状态定义 ```typescript const Form = (props: FormProps) => { // 表单数据状态 const [values, setValues] = useState(props.initialValues || {}); // 验证器映射 - 存储每个字段的验证函数 const validatorsMap = useRef(new Map()); ``` #### 核心方法实现 **① 字段值变更处理** ```typescript const onValueChange = (key: string, value: any) => { setValues((prevValues) => ({ ...prevValues, [key]: value })); }; ``` - 当任意字段值改变时,更新对应的表单数据 - 使用函数式更新确保状态不可变性 **② 验证器注册机制** ```typescript const handleValidateRegister = (name: string, cb: Function) => { validatorsMap.current.set(name, cb); }; ``` - 每个 Form.Item 将自己的验证函数注册到 Map 中 - 使用 useRef 避免重复创建 Map 对象 **③ 表单提交核心逻辑** ```typescript const handleSubmit = (e: FormEvent) => { e.preventDefault(); // 1. 收集所有注册的验证器 const validators = []; for (const [, validator] of validatorsMap.current) { if (typeof validator === "function") { validators.push(validator); } } // 2. 并行执行所有验证,传入最新的表单数据 Promise.all(validators.map((validator) => validator(values))).then( (results) => { // 3. 过滤出有错误的结果 const errorList = results.filter((item) => item !== ""); // 4. 根据验证结果执行对应回调 if (errorList.length) { props.onFinishFailed?.(errorList); // 有错误,执行失败回调 } else { props.onFinish?.(values); // 无错误,执行成功回调 } } ); }; ``` **提交流程分析:** 1. 阻止表单默认提交行为 2. 从 validatorsMap 中收集所有验证函数 3. 使用 Promise.all 并行执行验证(性能优化) 4. 汇总验证结果,决定成功或失败回调 #### Context 数据提供 ```typescript return (
{props.children}
); ``` ### 3. Item 组件 - 表单项核心 **核心职责:** 子组件增强、验证处理、状态同步 #### 状态和Context获取 ```typescript const Item = (props: ItemProps) => { const [errorInfo, setErrorInfo] = useState(null); const [value, setValue] = useState(""); const { onValueChange, validateRegister, values } = useContext(FormContext); ``` #### 关键技术1:动态属性注入 **问题:** 不同类型的表单控件使用不同的属性名接收值 - input 使用 `value` 属性 - checkbox 使用 `checked` 属性 **解决方案:** ```typescript // 动态计算要注入的属性 const propsName: Record = {}; if (props.valuePropName) { propsName[props.valuePropName] = value; // 自定义属性名,如 checked } else { propsName.value = value; // 默认使用 value } ``` #### 关键技术2:子组件克隆与增强 **核心思路:** 使用 React.cloneElement 为任意子组件注入属性和事件 ```typescript const elChildren = React.Children.toArray(props.children).length > 0 ? React.cloneElement(props.children!, { ...propsName, // 注入值属性(value 或 checked) onChange: (e: ChangeEvent) => { const newValue = getValueFromEvent(e); setValue(newValue); // 更新本地状态 onValueChange?.(props.name!, newValue); // 通知表单状态 handleValidate(newValue); // 实时验证 }, }) : null; ``` **增强过程分析:** 1. 检查是否有子组件 2. 使用 cloneElement 克隆子组件 3. 注入值属性(根据 valuePropName 决定属性名) 4. 注入 onChange 事件处理函数 #### 关键技术3:事件值提取 **问题:** 不同类型的表单控件返回值的方式不同 ```typescript const getValueFromEvent = (e: React.ChangeEvent) => { const { target } = e; if (target.type === "checkbox") { return target.checked; // checkbox 返回 checked 状态 } return target.value; // 其他控件返回 value }; ``` #### 关键技术4:双重验证机制 **实时验证 - 输入时触发** ```typescript onChange: (e) => { const newValue = getValueFromEvent(e); setValue(newValue); onValueChange?.(props.name!, newValue); handleValidate(newValue); // 直接使用新输入的值进行验证 } ``` **批量验证 - 提交时触发** ```typescript useEffect(() => { if (props.rules && props.rules.length) { validateRegister?.(props.name!, (formValues) => { // 提交时验证函数,使用表单传入的最新值 return handleValidate(formValues[props.name!]); }); } }, [props.rules, props.name]); ``` **两种验证的区别:** - 实时验证:使用当前输入值,立即反馈 - 批量验证:使用表单完整数据,最终校验 #### 验证函数核心实现 ```typescript const handleValidate = (validateValue?: any): Promise => { return new Promise((resolve) => { if (Array.isArray(props.rules) && props.rules.length) { const validator = new Schema({ [props.name!]: props.rules }); // 智能值选择:优先使用传入值,其次使用表单值 const valueToValidate = validateValue !== undefined ? (typeof validateValue === "object" ? validateValue[props.name!] // 对象类型,取对应字段 : validateValue) // 基本类型,直接使用 : values?.[props.name!]; // 兜底使用表单值 validator.validate({ [props.name!]: valueToValidate }, (errors) => { setErrorInfo(errors?.[0]?.message ?? ""); // 设置错误信息 resolve(errors?.[0]?.message ?? ""); // 返回验证结果 }); } else { resolve(""); // 无验证规则,直接通过 } }); }; ``` #### 状态同步机制 ```typescript useEffect(() => { if (value !== values?.[props.name!]) { setValue(values?.[props.name!]); // 同步表单数据到本地状态 } }, [values, props.name, value, props.valuePropName]); ``` ### 4. 统一导出策略 **目标:** 实现 `` 的 API 设计 ```typescript // index.ts import Form from "./form"; import Item from "./item"; type InternalFormType = typeof Form; interface FormInterface extends InternalFormType { Item: typeof Item; } const MyForm = Form as FormInterface; MyForm.Item = Item; export default MyForm; ``` ## 完整数据流程 ### 用户输入流程 ``` 1. 用户在 input 中输入 "hello" 2. 触发 onChange 事件 3. getValueFromEvent 提取值 "hello" 4. setValue("hello") 更新本地状态 5. onValueChange("name", "hello") 通知表单更新 6. handleValidate("hello") 实时验证 7. 显示验证结果(成功/失败) ``` ### 表单提交流程 ``` 1. 用户点击提交按钮 2. handleSubmit 阻止默认行为 3. 从 validatorsMap 收集所有验证器 4. Promise.all 并行执行验证,传入完整表单数据 5. 每个验证器调用 handleValidate(formValues[fieldName]) 6. 汇总所有验证结果 7. 根据结果执行 onFinish 或 onFinishFailed ``` ## 关键问题与解决方案 ### 问题1:React 状态更新的异步性 **现象:** 输入第一个字符时立即显示验证错误 **根本原因:** ```typescript // 错误的做法 onChange: (e) => { const newValue = getValueFromEvent(e); setValue(newValue); // 异步更新,不会立即生效 onValueChange(props.name!, newValue); // 异步更新,不会立即生效 handleValidate(values); // 使用的还是旧的 values } ``` **正确解决:** ```typescript // 正确的做法 onChange: (e) => { const newValue = getValueFromEvent(e); setValue(newValue); onValueChange(props.name!, newValue); handleValidate(newValue); // 直接使用新输入的值 } ``` **核心原理:** React 状态更新是异步的,在同一个事件处理函数中,状态值还是旧的。必须直接使用新值进行验证。 ### 问题2:验证时机的统一处理 **挑战:** 实时验证和提交验证需要使用不同的数据源 **解决策略:** ```typescript const handleValidate = (validateValue?: any): Promise => { // 智能值选择策略 const valueToValidate = validateValue !== undefined ? (typeof validateValue === "object" ? validateValue[props.name!] : validateValue) : values?.[props.name!]; // 使用统一的验证逻辑 }; ``` - 输入时:传入新输入值 `handleValidate(newValue)` - 提交时:传入表单对象 `handleValidate(formValues)` - 兜底:使用 Context 中的值 ### 问题3:TypeScript 类型安全 **类型冲突:** 验证函数需要同时支持单个值和对象参数 **解决方案:** ```typescript // 使用 any 类型,在函数内部进行类型判断 const handleValidate = (validateValue?: any): Promise => { const valueToValidate = validateValue !== undefined ? (typeof validateValue === "object" ? validateValue[props.name!] // 对象类型处理 : validateValue) // 基本类型处理 : values?.[props.name!]; // 兜底处理 }; ``` ## 性能优化要点 ### 1. useRef 优化 ```typescript // 使用 useRef 存储验证器,避免重复创建 const validatorsMap = useRef(new Map()); ``` ### 2. 并行验证 ```typescript // 使用 Promise.all 并行执行所有验证,而不是串行 Promise.all(validators.map((validator) => validator(values))) ``` ### 3. 精确依赖 ```typescript // useEffect 使用精确的依赖数组,避免不必要的重新执行 }, [values, props.name, value, props.valuePropName]); ``` ### 4. 函数式更新 ```typescript // 使用函数式更新确保状态更新的正确性 setValues((prevValues) => ({ ...prevValues, [key]: value })); ``` ## 使用示例 ```typescript console.log('成功:', values)} onFinishFailed={(errors) => console.log('失败:', errors)} > 同意协议 ``` ## 核心技术总结 这个表单组件体现了以下核心技术: 1. **Context + Hook 状态管理** - 跨组件数据共享 2. **React.cloneElement 组件增强** - 动态注入属性和事件 3. **async-validator 验证集成** - 强大的验证能力 4. **Promise 并发处理** - 性能优化的验证策略 5. **TypeScript 类型安全** - 完整的类型系统 6. **React 状态异步处理** - 正确处理状态更新时机 通过这些技术的巧妙组合,实现了一个功能完整、性能良好的表单组件系统。