使用shadcn封装的react-hook-form时useFormContext为null的问题
解决shadcn + react-hook-form中useFormContext为null的问题
问题复现
在项目中使用shadcn封装的react-hook-form时,多数场景正常,但某一特定组件运行时抛出useFormContext() is null错误,错误发生在useFormField函数的const { getFieldState, formState } = useFormContext();行。将该组件导入其他页面也会触发相同错误,但其他类似使用场景无异常。组件代码如下:
<Form {...form}> <FormField control={form.control} name={fieldName} render={({ field }) => ( <FormItem> <FormControl> <Input type="number" {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> </Form>
排查与解决方法
1. 确认Form组件的导入来源
确保你使用的Form组件是shadcn/ui提供的集成了react-hook-form上下文的版本,而非自定义或其他库的Form组件。正确导入路径示例:
import { Form } from '@/components/ui/form';
shadcn的Form组件内部封装了react-hook-form的FormProvider,只有通过它包裹的子组件才能获取到表单上下文。
2. 检查form实例的创建与传递
- 确保
form实例是通过react-hook-form的useForm()钩子正确创建的,且在组件渲染阶段同步生成:const form = useForm({ defaultValues: { [fieldName]: 0, // 为目标字段设置合理默认值 }, }); - 确认
<Form {...form}>传递了完整的form实例属性,使用展开运算符通常可覆盖所有必要属性,无需手动拆分传递。
3. 确保FormField始终在Form上下文范围内
- 检查是否存在条件渲染导致FormField在Form未渲染时提前渲染,比如:
// 错误示例:FormField可能在isLoading为true时脱离Form上下文 {isLoading ? <FormField ... /> : <Form {...form}><FormField ... /></Form>} - 若组件使用了React Portal,FormField被渲染到DOM的其他位置,会直接脱离Form的上下文范围,需调整组件结构避免这种情况。
4. 检查版本兼容性与依赖冲突
- 当前使用的react-hook-form版本为7.54.2,React版本为19.0.0,确认shadcn/ui的版本是否适配这两个版本。部分旧版shadcn/ui可能对React 19或最新react-hook-form版本支持不足,建议升级shadcn/ui到最新稳定版。
- 执行
pnpm list react-hook-form查看项目中是否存在多个版本的react-hook-form,版本冲突会导致上下文无法共享。可通过package.json的resolutions字段统一版本:"pnpm": { "resolutions": { "react-hook-form": "7.54.2" } }
5. 排查组件嵌套与上下文污染
检查该特定组件是否被嵌套在另一个Form组件内部,多层Form嵌套可能导致上下文冲突或覆盖。若存在这种情况,需调整组件结构,确保每个FormField只属于一个Form上下文。
修正后的示例代码
import { useForm } from 'react-hook-form'; import { Form, FormField, FormItem, FormControl, FormMessage } from '@/components/ui/form'; import { Input } from '@/components/ui/input'; export function TargetFormComponent({ fieldName }) { // 正确创建form实例 const form = useForm({ defaultValues: { [fieldName]: 0, }, }); return ( // 用shadcn的Form组件包裹所有表单字段 <Form {...form}> <FormField control={form.control} name={fieldName} render={({ field }) => ( <FormItem> <FormControl> <Input type="number" {...field} /> </FormControl> <FormMessage /> </FormItem> )} /> <button type="submit">提交表单</button> </Form> ); }
内容的提问来源于stack exchange,提问作者Eino Gourdin
相关产品推荐
相关产品推荐

