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

React Hook Form中Redux返回Null引发的非受控输入转受控错误

问题解决思路

出现「非受控输入转为受控」错误的核心原因是:组件初始化时,defaultValues里的字段值为undefined(因为data初始为null,data?.firstName返回undefined),此时react-hook-form会将输入视为非受控组件;当Redux数据加载完成后调用reset传入有效值,输入组件从非受控转为受控,触发报错。

具体修复步骤:

  • 初始化defaultValues为受控状态:给所有表单字段设置初始空字符串,确保组件一开始就是受控模式,避免后续状态切换。
  • 条件触发reset:只有当data存在(即Redux数据加载完成)时才调用reset,避免在data为null时重置出undefined值。
  • 修正字段名不一致问题:Discard按钮里的data?.first_name要改为data?.firstName,和表单字段名保持一致。

修改后的完整代码:

// ** React Imports
import { useEffect, Fragment } from "react";

// ** Third Party Components
import { useForm, Controller } from "react-hook-form";

// ** Reactstrap Imports
import {
  Row,
  Col,
  Form,
  Card,
  Input,
  Label,
  Button,
  CardBody,
  CardTitle,
  CardHeader,
  FormFeedback,
} from "reactstrap";

// ** Store & Actions
import { useDispatch, useSelector } from "react-redux";
import { getUserProfile } from "@store/userProfile";

const UserProfile = () => {
  // ** Store Vars
  const dispatch = useDispatch();
  const { data } = useSelector((state) => state.userProfile);

  //** ComponentDidMount
  useEffect(() => {
      dispatch(getUserProfile());
  }, [dispatch]);

  // ** Hooks
  // 初始化空字符串,确保表单一开始就是受控状态
  const defaultValues = {
    firstName: '',
    lastName: '',
    email: '',
  };

  const {
    control,
    setValue,
    setError,
    handleSubmit,
    reset,
    formState: { errors },
  } = useForm({
    defaultValues,
  });

  //** 仅当data存在时执行reset
  useEffect(() => {
    if (data) {
      reset({
        firstName: data.firstName,
        lastName: data.lastName,
        email: data.email,
      });
    }
  }, [data, reset]);

  // 补充onSubmit函数(原代码遗漏)
  const onSubmit = (formData) => {
    // 处理表单提交逻辑
    console.log(formData);
  };

  return (
    <Fragment>
      <Card>
        <CardHeader className="border-bottom py-2">
          <CardTitle tag="h4">User Account</CardTitle>
        </CardHeader>
        <CardBody className="py-2 my-25">
          <Form className="mt-2 pt-50" onSubmit={handleSubmit(onSubmit)}>
            <Row>
              <Col sm="6" className="mb-1">
                <Label className="form-label" for="firstName">
                  First Name
                </Label>
                <Controller
                  name="firstName"
                  control={control}
                  render={({ field }) => (
                    <Input
                      id="firstName"
                      placeholder="John"
                      invalid={!!errors.firstName}
                      {...field}
                    />
                  )}
                />
                {errors.firstName && (
                  <FormFeedback>Please enter a valid First Name</FormFeedback>
                )}
              </Col>
              <Col sm="6" className="mb-1">
                <Label className="form-label" for="lastName">
                  Last Name
                </Label>
                <Controller
                  name="lastName"
                  control={control}
                  render={({ field }) => (
                    <Input
                      id="lastName"
                      placeholder="Doe"
                      invalid={!!errors.lastName}
                      {...field}
                    />
                  )}
                />
                {errors.lastName && (
                  <FormFeedback>Please enter a valid Last Name</FormFeedback>
                )}
              </Col>
              <Col sm="6" className="mb-1">
                <Label className="form-label" for="login-email">
                  Email
                </Label>
                <Controller
                  id="email"
                  name="email"
                  control={control}
                  render={({ field }) => (
                    <Input
                      type="email"
                      placeholder="john@example.com"
                      invalid={!!errors.email}
                      {...field}
                    />
                  )}
                />
                {errors.email && (
                  <FormFeedback>{errors.email.message}</FormFeedback>
                )}
              </Col>
              <Col className="mt-2" sm="12">
                <Button type="submit" className="me-1" color="primary">
                  Save changes
                </Button>
                <Button
                  color="secondary"
                  outline
                  onClick={() => {
                    // 修正字段名,和data里的key保持一致
                    if (data) {
                      setValue("firstName", data.firstName);
                      setValue("lastName", data.lastName);
                      setValue("email", data.email);
                    }
                  }}
                >
                  Discard
                </Button>
              </Col>
            </Row>
          </Form>
        </CardBody>
      </Card>
    </Fragment>
  );
};

export default UserProfile;

额外说明:

  • 原代码中遗漏了onSubmit函数,已补充完整,否则表单提交会报错。
  • 将errors.firstName && true简化为!!errors.firstName,逻辑更清晰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 14:10:23