import React from 'react'; import { FieldValues, Control, FieldPath, RegisterOptions, } from 'react-hook-form'; import Field from '../Field'; import { FieldProps } from '../Field/interface'; import { useLocale } from '../Locale/locale'; import FocusComponent from './Focus'; import { useFormField } from './hooks/useFormField'; import { FormFieldProvider } from './hooks/FormFieldContext'; export interface FormItemProps extends Omit { /** * 表单字段名称 */ name: FieldPath; /** * 表单控制器,通常从 useFormContext 获取 */ control?: Control; /** * 表单验证规则 */ rules?: RegisterOptions; /** * 子组件 */ children: React.ReactElement; /** * 自定义错误信息 */ customError?: string; /** * 是否自动显示错误信息(以Toast方式) */ showToast?: boolean; /** * 是否在 onChange 时触发校验 * 适用于没有 onBlur 事件的组件(如选择器、开关等) */ triggerValidationOnChange?: boolean; /** * 假值时兜底传递的值 */ fallbackValue?: unknown; } const FormItem = ({ name, control, rules, children, customError, message, requiredMark = true, showToast = true, triggerValidationOnChange = false, fallbackValue, ...fieldProps }: FormItemProps) => { const locale = useLocale().Form; const { field, fieldState, errorMessage, hasError } = useFormField({ name, control, rules, customError, }); const status = errorMessage && !children.props.status ? 'error' : children.props.status; // 准备FormFieldContext的值 const formFieldContextValue = { field, fieldState, errorMessage, hasError, }; // 克隆子组件并注入表单相关属性 const childWithProps = React.cloneElement(children, { value: field.value, ...children.props, onChange: (value: unknown) => { // 调用原有的 onChange children.props.onChange?.(value); // 调用 react-hook-form 的 onChange field.onChange(value ?? fallbackValue); // 如果设置了 triggerValidationOnChange,在 onChange 时主动触发校验 if (triggerValidationOnChange) { field.onBlur(); } }, onBlur: (event: unknown) => { // 调用原有的 onBlur children.props.onBlur?.(event); // 调用 react-hook-form 的 onBlur field.onBlur(); }, // 如果子组件支持 status 属性,在有错误时设置为 error status: status, inputProps: { ...children.props.inputProps, status: status, }, }); return ( } > {childWithProps} ); }; export default FormItem;