组件外声明的React Context值为undefined的问题求助
问题原因与解决方案
核心问题
你遇到的Context传递失效,本质是组件渲染上下文的问题:当你把AbstractField作为children直接传入AbstractForm时,这些子组件在父组件(比如UserEditForm)的上下文中就已经完成了初始化,它们的useContext(ControlContext)会直接获取到Context的默认值{},而不会等待AbstractForm内部的Provider生效。
另外,你用useState和useEffect保存control是多余的——useForm返回的control本身就是稳定的引用,不需要额外的状态同步。
修复方案
1. 修改AbstractForm的Props定义,改用渲染函数替代直接传children
将children换成一个返回JSX的函数,让子组件在AbstractForm内部的Provider作用域内渲染:
interface AbstractFormProps<T extends DataTypes> { alert: { title: string; message: string; }; dialog: { title: string; message: string; trigger: JSX.Element; }; form: { data?: T; schema: ZodObject<any>; onSubmit: (...args: any[]) => any; onError: (...args: any[]) => any; }; renderFields: () => JSX.Element; // 替换children为渲染函数 }
2. 简化AbstractForm的实现,去掉多余的状态管理
直接使用useForm返回的control,不需要useState和useEffect:
function AbstractForm<T extends DataTypes>(props: AbstractFormProps<T>) { const { renderFields, alert, dialog, form } = props; const _form = useForm<T>({ resolver: zodResolver(form.schema), defaultValues: form.data as DefaultValues<T>, }); // 直接使用useForm返回的control,无需额外状态 const control = _form.control; return ( <> <ControlContext.Provider value={control}> <form> <AbstractDialog trigger={dialog.trigger} title={dialog.title} description={dialog.message} > <> {/* 在Provider内部调用渲染函数,让子组件获取正确的Context */} {renderFields()} <ConfirmDialog title={alert.title} message={alert.message} onConfirm={_form.handleSubmit(form.onSubmit, form.onError)} trigger={<Button>Submit</Button>} /> </> </AbstractDialog> </form> </ControlContext.Provider> </> ); }
3. 更新表单使用方式,传入渲染函数
在UserEditForm中,用renderFields属性传递渲染函数替代直接传入children:
function UserEditForm({ trigger, user }: { trigger: JSX.Element, user: User }) { return ( <AbstractForm alert={{ title: "Are you sure you wish to update this User?", message: "This action is irreversible.", }} dialog={{ title: "Edit User", message: "Edit an existing user", trigger: trigger, }} form={{ schema: userSchema, onSubmit: UserForm.onSubmit, onError: UserForm.onError }} renderFields={() => ( <AbstractField field={UserFormFields.get("_id")!} /> )} /> ); }
原理说明
通过渲染函数,AbstractField会在AbstractForm内部的ControlContext.Provider生效后才被渲染,此时useContext(ControlContext)就能正确获取到react-hook-form的control对象,而不是默认的空对象。同时去掉多余的状态管理,避免了不必要的组件重渲染和状态同步问题。
内容的提问来源于stack exchange,提问作者stealing_society
相关产品推荐
相关产品推荐

