You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Hook Form:传递register和handleSubmit作为props时报非函数错误

React Hook Form 传递register/handleSubmit报错:TypeError: register和handleSubmit不是函数

问题描述

我想用react-hook-form做输入验证,输入内容会随选择项变化,所以需要从父组件给子组件传递register和handleSubmit,但传完之后React抛出错误:TypeError: register和handleSubmit不是函数。我已经从最新版本切换到react-hook-form V6.12.2,问题仍未解决。

Parent.tsx

export const RequestForm =()=>{
    const { handleSubmit , register} = useForm()
    const handleRegistration = (data:any) => console.log(data,);
   
    return (
    <div  className="request_form">
     <form onSubmit={handleSubmit(handleRegistration)}>
        {(selectedServicesTypes == "Transcriptions - Audio/Video")
            ?
    (<RequestTranscriptions handleRegistration={handleRegistration}
                                       register={register}
                                       handleSubmit={handleSubmit}/>)
            :
            (selectedServicesTypes== "Closed Captions - English")
             ?
            (<RequestClosedCaptions/>) 
             : 
            (selectedServicesTypes == "Global Subtitles")  
             ? 
             (<RequestGlobalSubtitles/>)
             : 
             (<NewReq />)
             }
      </form>
    </div>
    )}

可复用组件Select.tsx

export const PrimarySelect:React.FC<SelectProps> =({onChange,className, values, handleRegistration,register,handleSubmit})=>{
  const getSelection = useSelector(transactionReqSelectors.selectService)
   return (
       <FormControl sx={{ m: 1, }}>
         <InputLabel id="demo-multiple-name-label">Select</InputLabel>
            <Select
                value={getSelection}
                className={className}
                name="Select"
     // 当我改为ref={register}时不会报错,但无法获取值。
     // 当我写ref={register("Select")}时会报错。
                ref={register("Select")}
                labelId="demo-multiple-name-label"
                id="demo-multiple-name"
                onChange={onChange}
                input={<OutlinedInput label="Select" />}>
                {values?.map((name) => (
                    <MenuItem
                        ref={register}
                        value={name.type}
                    >
                        {name.type}
                    </MenuItem>
                ))}
            </Select>
        </FormControl>
)}

错误原因及修复方案

1. 核心问题:props传递断层

你在RequestForm里给RequestTranscriptions传递了register和handleSubmit,但**RequestTranscriptions没有把这两个props继续传递给它内部的PrimarySelect组件**,导致PrimarySelect里的register是undefined,调用register("Select")自然会触发"不是函数"的错误。

修复步骤:
修改RequestTranscriptions组件,确保它将父组件传入的register等props转发给PrimarySelect:

// 示例RequestTranscriptions代码
export const RequestTranscriptions = ({ register, handleRegistration }) => {
  return (
    <div>
      {/* 其他表单内容 */}
      <PrimarySelect 
        register={register}
        handleRegistration={handleRegistration}
        onChange={/* 你的onChange逻辑 */}
        className="your-class"
        values={/* 你的选项数据 */}
      />
    </div>
  )
}

2. Material-UI Select与react-hook-form v6的适配问题

react-hook-form v6中,Material-UI的Select是受控组件,不能直接给Select标签绑定ref={register("xxx")},需要通过两种方式适配:

方案一:使用Controller组件(推荐)

Controller是react-hook-form专门用来适配第三方受控组件的工具,能自动处理值同步和验证:

// 先导入Controller
import { Controller } from "react-hook-form";

export const PrimarySelect:React.FC<SelectProps> =({onChange,className, values, control})=>{
  const getSelection = useSelector(transactionReqSelectors.selectService)
  
  return (
      <FormControl sx={{ m: 1, }}>
        <InputLabel id="demo-multiple-name-label">Select</InputLabel>
        <Controller
          name="Select" // 表单数据的key,会出现在handleRegistration的data中
          control={control} // 从父组件传递的useForm返回的control对象
          defaultValue=""
          render={({ field: { onChange: rhfOnChange, value } }) => (
            <Select
              value={value || getSelection}
              className={className}
              labelId="demo-multiple-name-label"
              id="demo-multiple-name"
              onChange={(e) => {
                rhfOnChange(e.target.value); // 同步到react-hook-form
                if (onChange) onChange(e); // 触发父组件的onChange
              }}
              input={<OutlinedInput label="Select" />}>
              {values?.map((name) => (
                <MenuItem key={name.type} value={name.type}>
                  {name.type}
                </MenuItem>
              ))}
            </Select>
          )}
        />
      </FormControl>
  )}

注意:此时父组件需要传递control(从useForm()返回)而非register给子组件,或者使用FormProvider全局共享form上下文,避免层层传递props。

方案二:手动绑定register到内部input

如果不想用Controller,可以将register的属性拆分后绑定到Select内部的OutlinedInput上:

export const PrimarySelect:React.FC<SelectProps> =({onChange,className, values, register})=>{
  const getSelection = useSelector(transactionReqSelectors.selectService)
  // 解构register返回的属性
  const { ref, ...registerProps } = register("Select")
  
  return (
      <FormControl sx={{ m: 1, }}>
        <InputLabel id="demo-multiple-name-label">Select</InputLabel>
        <Select
          value={getSelection}
          className={className}
          labelId="demo-multiple-name-label"
          id="demo-multiple-name"
          onChange={(e) => {
            registerProps.onChange(e); // 同步到react-hook-form
            if (onChange) onChange(e); // 触发父组件的onChange
          }}
          // 将ref和register属性绑定到内部的input上
          input={<OutlinedInput label="Select" ref={ref} {...registerProps} />}>
          {values?.map((name) => (
            <MenuItem key={name.type} value={name.type}>
              {name.type}
            </MenuItem>
          ))}
        </Select>
      </FormControl>
  )}

3. 额外优化点

  • 不需要给MenuItem加ref={register},完全无效,register只需绑定到表单控件本身。
  • 父组件的handleSubmit已经在<form>的onSubmit中使用,无需传递给子组件,子组件只需做好值的收集即可。

内容的提问来源于stack exchange,提问作者Diana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:25:34