如何在react-hook-form中同时填充ref并注册文件输入组件?
解决React Hook Form结合自定义Chakra UI文件输入组件的取值问题
问题根源分析
你的代码存在几个关键问题导致无法获取文件值并触发报错:
- Controller与手动register冲突:自定义组件里同时使用
Controller和手动调用register,React Hook Form会尝试将你传入的File对象回写到input的files属性(该属性是只读的),从而触发类型错误。 - 未传递control给UploadUnit:
FileUpload组件调用UploadUnit时没有传入useForm返回的control,导致Controller无法正常关联表单控制。 - defaultValue设置错误:文件输入的默认值应该是
null而非空字符串,空字符串无法和File对象类型匹配。 - 冗余的字段注册:
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
相关产品推荐
相关产品推荐

