React中如何将作为children传入的输入框注册到useForm并取值
首先注意:所有需要注册的输入组件必须设置唯一的name属性,这是react-hook-form识别字段的必要前提。
方案1:遍历子元素注入注册属性(适合输入组件为MyForm直接子节点的简单场景)
你可以通过React.Children.map遍历传入的children,用React.cloneElement给符合条件的输入组件注入register返回的相关属性,全程用函数式组件实现,不需要类组件。
修改后的MyForm组件代码:
import React from 'react'; import { useForm, RegisterOptions } from 'react-hook-form'; import { TextField } from '@mui/material'; type MyFormProps = { children?: React.ReactNode // 可选:支持传入表单默认值等useForm配置 defaultValues?: Record<string, any> } const MyForm: React.FC<MyFormProps > = (props) => { const { register, handleSubmit, formState: { errors } } = useForm({ defaultValues: props.defaultValues }); const onSubmit = handleSubmit(data => console.log(data)); // 遍历处理子元素 const processedChildren = React.Children.map(props.children, child => { // 只处理合法的React元素,且是TextField类型(可以根据你的需求扩展支持的组件类型) if (React.isValidElement(child) && child.type === TextField) { const inputName = child.props.name; // 没有name的输入组件不注册 if (!inputName) return child; // 注入register返回的属性,同时保留原有属性 return React.cloneElement(child, { ...register(inputName), // 可选:注入错误提示 error: !!errors[inputName], helperText: errors[inputName]?.message as string }); } // 非输入组件直接返回,比如你传入的Button return child; }) return ( <div> <form onSubmit={onSubmit}> {processedChildren} </form> </div> ); } export default MyForm;
客户端App.tsx使用时只需给每个TextField加name属性即可:
import React from 'react'; import { TextField, Button } from '@mui/material'; import {MyForm} from 'react-myform'; function App() { return ( <div> <MyForm defaultValues={{username: '', email: ''}}> <TextField name="username" label="用户名"/> <TextField name="email" label="邮箱"/> <Button type="submit" variant="contained">提交</Button> </MyForm> </div> ); } export default App;
方案2:基于Context实现(适合输入组件嵌套在其他组件内的复杂场景)
如果你的输入组件不是MyForm的直接子节点,遍历children的方案会失效,此时可以用react-hook-form内置的Context能力实现,灵活度更高:
修改后的MyForm组件代码:
import React from 'react'; import { useForm, FormProvider } from 'react-hook-form'; type MyFormProps = { children?: React.ReactNode defaultValues?: Record<string, any> onSubmit?: (data: Record<string, any>) => void } const MyForm: React.FC<MyFormProps > = (props) => { const methods = useForm({ defaultValues: props.defaultValues }); const handleFormSubmit = methods.handleSubmit(data => { if (props.onSubmit) props.onSubmit(data); else console.log(data); }); return ( <div> <FormProvider {...methods}> <form onSubmit={handleFormSubmit}> {props.children} </form> </FormProvider> </div> ); } export default MyForm;
你可以额外封装一个适配MUI TextField的表单项组件,方便用户直接使用:
// 你的node模块里的FormField组件 import React from 'react'; import { useFormContext, RegisterOptions } from 'react-hook-form'; import { TextField, TextFieldProps } from '@mui/material'; type FormFieldProps = TextFieldProps & { name: string rules?: RegisterOptions } export const FormField: React.FC<FormFieldProps> = ({name, rules, ...rest}) => { const { register, formState: { errors } } = useFormContext(); return ( <TextField {...rest} {...register(name, rules)} error={!!errors[name]} helperText={errors[name]?.message as string} /> ) }
客户端使用示例:
import React from 'react'; import { Button } from '@mui/material'; import {MyForm, FormField} from 'react-myform'; function App() { return ( <div> <MyForm defaultValues={{username: '', email: ''}} onSubmit={(data) => console.log('提交数据:', data)} > {/* 哪怕嵌套在其他组件内也能正常工作 */} <div> <FormField name="username" label="用户名" rules={{required: '请输入用户名'}}/> <FormField name="email" label="邮箱" rules={{required: '请输入邮箱'}}/> </div> <Button type="submit" variant="contained">提交</Button> </MyForm> </div> ); } export default App;
关于ref的疑问
不需要你自己单独维护ref实现注册,react-hook-form的register方法返回值已经包含了ref绑定逻辑,透传给输入组件后会自动完成关联,完全符合函数式组件的开发习惯。
内容的提问来源于stack exchange,提问作者Markus Profanter
相关产品推荐
相关产品推荐

