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

如何在react-hook-form中同时填充ref并注册文件输入组件?

解决React Hook Form结合自定义Chakra UI文件输入组件的取值问题

问题根源分析

你的代码存在几个关键问题导致无法获取文件值并触发报错:

  1. Controller与手动register冲突:自定义组件里同时使用Controller和手动调用register,React Hook Form会尝试将你传入的File对象回写到input的files属性(该属性是只读的),从而触发类型错误。
  2. 未传递control给UploadUnit:FileUpload组件调用UploadUnit时没有传入useForm返回的control,导致Controller无法正常关联表单控制。
  3. defaultValue设置错误:文件输入的默认值应该是null而非空字符串,空字符串无法和File对象类型匹配。
  4. 冗余的字段注册:FileUpload里手动调用register({name: "trn", type:"custom"})完全多余,Controller会自动处理字段注册。

修正后的UploadUnit组件代码

import { InputGroup, InputLeftElement, Input, Icon } from "@chakra-ui/react";
import { Controller } from "react-hook-form";
import { FiFile } from "react-icons/fi";
import { useRef } from "react";

export default function UploadUnit({ name, control, acceptedFileTypes, placeholder }) {
  const inputRef = useRef(null);

  return (
    <Controller
      name={name}
      control={control}
      defaultValue={null} // 修正默认值为null
      render={({ field: { onChange, value, name } }) => {
        return (
          <InputGroup>
            <InputLeftElement pointerEvents="none">
              <Icon as={FiFile} />
            </InputLeftElement>
            {/* 移除手动register,Controller已处理字段绑定 */}
            <input
              name={name}
              type="file"
              onChange={(e) => onChange(e.target.files[0])}
              accept={acceptedFileTypes}
              ref={inputRef} // 直接绑定ref
              style={{ display: "none" }}
            />
            <Input
              placeholder={placeholder || "Your file ..."}
              onClick={() => inputRef.current?.click()}
              readOnly={true}
              value={(value && value.name) || ""}
            />
          </InputGroup>
        );
      }}
    />
  );
}

修正后的FileUpload组件代码

import React from "react";
import { Button, ButtonGroup } from "@chakra-ui/react";
import { useForm } from "react-hook-form";
import UploadUnit from "./UploadUnit";
import storage from "./fireBaseConfig.js";

export const FileUpload = ({ name }) => {
  const { register, handleSubmit, control } = useForm();

  const onSubmit = (data) => {
    console.log("List:" + data.testing[0].name); // 正常输出
    console.log("List:" + data.trn?.name); // 现在可正确获取文件名,注意这里是单个File对象而非数组
    console.log("List:" + data.nis?.name);
    const storageRef = storage;
  };

  // 移除冗余的register调用
  return (
    <>
      <form onSubmit={handleSubmit(onSubmit)}>
        <br />
        <div>
          <input type="file" name="testing" ref={register} />
        </div>
        <br />
        <div>
          {/* 必须传递control给UploadUnit */}
          <UploadUnit name="trn" control={control} />
        </div>
        <br />
        <br />
        <div>
          <UploadUnit name="nis" control={control} />
        </div>
        <br />
        <br />
        <ButtonGroup spacing={4}>
          <Button loadingText="Submitting" type="submit">
            Save Progress
          </Button>
          <Button variant="outline">Next</Button>
        </ButtonGroup>
      </form>
    </>
  );
};

export default FileUpload;

额外说明

  • 若需支持多文件上传,只需给input添加multiple属性,同时将onChange改为onChange(e => onChange(e.target.files)),此时data.trn会是FileList对象,需用data.trn[0].name访问文件名。
  • 代码中使用data.trn?.name的可选链操作符,避免未选择文件时出现Cannot read properties of undefined错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:25:24