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

React Hook Form: useController报methods is null错误解决及Form Provider用法

一、解决"methods is null"报错

这个错误的核心原因是useController没有关联到有效的React Hook Form实例。你当前代码中先调用useController,但此时useForm还未执行,没有创建表单上下文,导致useController找不到对应的表单实例,从而抛出报错。

修复步骤

  1. 先调用useForm初始化表单实例,获取control对象
  2. 将control传入useController的配置参数中,关联当前表单实例

修改后的代码示例:

import React from 'react';
import { useController, useForm } from 'react-hook-form';

interface formTypes {
    fullname: string,
    username: string,
    email: string,
    password: string,
    confirmPassword: string,
    phone: string,
    gender: string,
    countries: string,
    city: string // 补充select用到的city字段
}

const NewForm = () => {
    // 1. 优先初始化useForm,拿到表单控制对象control
    const { register, handleSubmit, formState: { errors }, control } = useForm<formTypes>({
        defaultValues: { // 可选:设置表单默认值
            fullname: '',
            gender: ''
        }
    });

    // 2. 传入control,关联当前表单实例
    const { field } = useController({
        name: 'fullname', 
        control, 
        rules: {
            required: true,
            pattern: /^[a-zA-Z0-9]+$/i
        }
    })

    const onSubmit = (data: formTypes) => console.log(data);

    return (
        <>
            <form onSubmit={handleSubmit(onSubmit)}>
                <input {...field} placeholder="fullname" />
                {errors.fullname && <span>请输入合法的全名</span>}
                
                <input type="password" placeholder="password" {...register("password", { required: true, minLength: 4 })} />
                {errors.password && <span>密码至少4位</span>}
                
                <input type="password" placeholder="confirmPassword" {...register("confirmPassword", { maxLength: 5 })} />
                
                <input {...register("gender", { required: true })} type="radio" value="male" />
                <input {...register("gender", { required: true })} type="radio" value="female" />
                {errors.gender && <span>请选择性别</span>}
                
                <input type="email" placeholder="email" {...register("email", { pattern: /(^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$)/i })} />
                {errors.email && <span>请输入合法邮箱</span>}
                
                <input type="tel" placeholder="phone" {...register("phone", { pattern: /(^(\+98|0)?\d{10}$)/i })} />
                
                <select {...register("city")}>
                    <option value="tehran">tehran</option>
                    <option value="shiraz">shiraz</option>
                    <option value="tabriz">tabriz</option>
                </select>

                <input type="submit" />
            </form>
        </>
    );
}

export default NewForm

二、使用React Hook Form的Form Provider

当需要在多层组件间共享同一个表单状态(比如父组件和嵌套子组件都要访问表单实例),可以用FormProvider和useFormContext实现,避免手动传递表单方法。

实现步骤

  1. 在根表单组件中调用useForm,获取完整的表单方法集合
  2. 用FormProvider包裹所有需要共享表单状态的子组件,将表单方法作为value传入
  3. 在子组件中通过useFormContext获取表单实例,无需手动传递props

示例代码

父组件(表单根组件)
import React from 'react';
import { useForm, FormProvider } from 'react-hook-form';
import FullnameInput from './FullnameInput';
import PasswordInput from './PasswordInput';

interface formTypes {
    fullname: string,
    password: string,
    gender: string
}

const ParentForm = () => {
    const formMethods = useForm<formTypes>({
        defaultValues: {
            fullname: '',
            password: '',
            gender: ''
        }
    });

    const onSubmit = (data: formTypes) => console.log(data);

    return (
        <FormProvider value={formMethods}>
            <form onSubmit={formMethods.handleSubmit(onSubmit)}>
                <FullnameInput />
                <PasswordInput />
                <input {...formMethods.register("gender", { required: true })} type="radio" value="male" />
                <input {...formMethods.register("gender", { required: true })} type="radio" value="female" />
                
                <input type="submit" />
            </form>
        </FormProvider>
    );
}

export default ParentForm;
子组件(FullnameInput.tsx)
import React from 'react';
import { useController, useFormContext } from 'react-hook-form';
import type { formTypes } from './ParentForm';

const FullnameInput = () => {
    // 从上下文中获取表单控制对象
    const { control } = useFormContext<formTypes>();
    
    const { field, fieldState } = useController({
        name: 'fullname',
        control,
        rules: {
            required: "请输入全名",
            pattern: {
                value: /^[a-zA-Z0-9]+$/i,
                message: "全名只能包含字母和数字"
            }
        }
    });

    return (
        <>
            <input {...field} placeholder="fullname" />
            {fieldState.error && <span>{fieldState.error.message}</span>}
        </>
    );
}

export default FullnameInput;
子组件(PasswordInput.tsx)
import React from 'react';
import { useFormContext } from 'react-hook-form';
import type { formTypes } from './ParentForm';

const PasswordInput = () => {
    const { register, formState: { errors } } = useFormContext<formTypes>();

    return (
        <>
            <input 
                type="password" 
                placeholder="password" 
                {...register("password", { 
                    required: "密码不能为空", 
                    minLength: {
                        value: 4,
                        message: "密码至少4位"
                    }
                })} 
            />
            {errors.password && <span>{errors.password.message}</span>}
        </>
    );
}

export default PasswordInput;

FormProvider的优势

  • 避免多层组件间手动传递表单方法(control、register等)
  • 统一管理表单状态,所有子组件共享同一个表单实例

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 15:55:19