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

组件外声明的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 15:37:15