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

React多步表单useFormContext解构control为null报错排查修复

问题报错

运行项目抛出错误:
TypeError: Cannot destructure property 'control' of 'Object(...)(...)' as it is null

问题背景

开发React多步表单时耗时2天排查、查阅相关资料未解决。需求为实现表单必填校验:字段未输入内容时输入框标红,提示用户必填。当前代码运行直接抛出上述空值解构错误,校验功能无法正常生效。

现有代码片段

组件上下文引入代码

import { multiStepContext } from '../StepContext';
export default function BasicStep() {
  const {
    setStep,
    userData,
    setUserData,
    firstName,
    lastName,
    setfirstName,
    setlastName,
    username,
    title,
    cnicNo,
    gender,
    dob,
    referredBy,
    // country,
    cityId,
    state,
    profilePicture,
    setUsername,
    settitle,
    setcnicNo,
    setDob,
    // setCountry,
    // setState,
    setprofilePicture,
    setcityId,
    countryId,
    setcountryId,
    setreferredBy,
    stateId,
    setstateId,
    setGender
  } = useContext(multiStepContext);

  const {
    control,
    formState: { errors }
  } = useFormContext();

表单字段实现代码

<Controller
              control={control}
              name='firstName'
              rules={{
                required: true
                // type: 'email'
              }}
              render={({ field }) => (
    <TextField
                label='First Name'
                value={userData[firstName]}
                onChange={(e) => setfirstName(e.target.value)}
                margin='normal'
                defaultValue={userData[firstName]}
                fullWidth
                variant='outlined'
                {...field}
                error={errors.firstName}
              />

下一步按钮代码

<Button onClick={() => setStep(2)} variant='contained' color='primary'>
          Next
        </Button>
问题根因

useFormContext() 是react-hook-form提供的上下文钩子,只有组件被FormProvider包裹时才能拿到表单实例返回值,未包裹时返回值为null,直接解构null的control属性就会触发当前报错。
除此之外现有代码还存在两个会导致校验失效的问题:

  • Controller的render函数中重复绑定value、onChange、defaultValue属性,和{...field}自带的表单绑定逻辑冲突,会导致表单状态不同步
  • 下一步按钮直接触发步骤切换,没有先调用表单校验方法,就算修复了上下文问题,未填字段也不会阻断跳转
修复方案

1. 上层组件添加FormProvider包裹

在多步表单的最外层容器组件(管理step切换、持有全局状态的父组件)中,调用useForm创建表单实例,用FormProvider包裹所有子步骤组件,将表单实例透传给下层子组件:

import { useState } from 'react';
import { useForm, FormProvider } from 'react-hook-form';
import { multiStepContext } from './StepContext';
import BasicStep from './steps/BasicStep';
// 引入其他步骤组件

export default function MultiStepFormContainer() {
  // 创建表单实例,统一管理所有字段状态和校验规则
  const formMethods = useForm({
    defaultValues: {
      firstName: '',
      lastName: '',
      username: '',
      // 其余表单字段默认值在这里统一声明
    },
    mode: 'onBlur' // 失焦时就触发校验,体验更好
  });

  const [step, setStep] = useState(1);
  // 其余原有multiStepContext的状态逻辑保留

  return (
    <multiStepContext.Provider value={{
      setStep,
      // 其余原有上下文传值保留
    }}>
      {/* 所有步骤组件必须放在FormProvider内部 */}
      <FormProvider {...formMethods}>
        {step === 1 && <BasicStep />}
        {/* {step === 2 && <SecondStep />} 其余步骤按需渲染 */}
      </FormProvider>
    </multiStepContext.Provider>
  )
}

如果多步表单的步骤是独立路由页面,必须将FormProvider放在路由组件外层,不能随着路由切换卸载FormProvider,否则子组件依然拿不到表单上下文。

2. 修正表单字段绑定逻辑

删除重复的value、onChange、defaultValue绑定,表单状态统一交给react-hook-form管理,不需要单独为每个字段维护上下文状态,同时补充错误提示文案展示:

<Controller
  control={control}
  name='firstName'
  rules={{
    required: '请输入First Name' // 配置必填提示文案
  }}
  render={({ field, fieldState: { error } }) => (
    <TextField
      label='First Name'
      margin='normal'
      fullWidth
      variant='outlined'
      {...field}
      error={!!error} // 存在错误时输入框标红
      helperText={error?.message} // 展示对应错误提示
    />
  )}
/>

3. 修正下一步按钮跳转逻辑

点击按钮时先触发表单校验,校验通过后再执行步骤跳转:
首先在BasicStep组件中从useFormContext解构出handleSubmit方法:

const {
  control,
  formState: { errors },
  handleSubmit
} = useFormContext();

然后修改按钮点击事件:

<Button 
  onClick={handleSubmit(() => {
    // 所有字段校验通过后才会执行跳转逻辑
    setStep(2)
  })} 
  variant='contained' 
  color='primary'
>
  Next
</Button>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 08:57:21