问题描述
如何使用带有react-hook-form的react-dropzone(hook组件)来返回正确的文件而不仅仅是文件名
解决方法
我就是这样做的@Bill
const FileUpload = (props) => {
const {
control,label,labelClassName,name,isRequired,rules,error,multiple,maxFiles,setValue,accept,maxSize,setError,clearErrors,formGroupClassName,watch,} = props;
const [files,setFiles] = useState(watch(name));
const onDrop = useCallback(
(acceptedFiles,rejectedFiles) => {
if (rejectedFiles && rejectedFiles.length > 0) {
setValue(name,[]);
setFiles([]);
setError(name,{
type: 'manual',message: rejectedFiles && rejectedFiles[0].errors[0].message,});
} else {
setFiles(
acceptedFiles.map((file) =>
Object.assign(file,{
preview: URL.createObjectURL(file),}),),);
clearErrors(name);
acceptedFiles.forEach((file) => {
const reader = new FileReader();
reader.onabort = () => toastError('File reading was aborted');
reader.onerror = () => toastError('file reading has failed');
reader.readAsDataURL(file);
reader.onloadend = () => {
setValue(name,file,{ shouldValidate: true });
};
});
}
},[name,clearErrors],);
const deleteFile = (e,file) => {
e.preventDefault();
const newFiles = [...files];
newFiles.splice(newFiles.indexOf(file),1);
if (newFiles.length > 0) {
setFiles(newFiles);
} else {
setFiles(null);
setValue(name,null);
}
};
const thumbs =
files &&
files !== null &&
files.map((file) => {
const ext = file.name && file.name.substr(file.name.lastIndexOf('.') + 1);
return ext === 'pdf' ? (
<ul key={file.name} className="mt-2">
<li>{file.name}</li>
</ul>
) : (
<div className="thumb position-relative" key={file.name}>
<img src={file.preview ? file.preview : file} alt={file.name} />
<Button
className="trash-icon"
color="danger"
size="sm"
onClick={(e) => deleteFile(e,file)}
>
<FontAwesomeIcon icon={faTrashAlt} size="sm" />
</Button>
</div>
);
});
useEffect(() => {
if (
watch(name) !== '' &&
typeof watch(name) === 'string' &&
watch(name).startsWith('/')
) {
setFiles([
{
preview: getFileStorageBaseUrl() + watch(name),name: watch(name)
.substr(watch(name).lastIndexOf('/') + 1)
.substr(0,watch(name).lastIndexOf('/')),},]);
}
},[watch,name]);
useEffect(
() => () => {
if (files && files.length > 0) {
files.forEach((file) => URL.revokeObjectURL(file.preview));
}
},[files],);
const { getRootProps,getInputProps,isDragActive } = useDropzone({
maxFiles: multiple ? maxFiles : 0,onDrop,minSize: 0,});
return (
<div className={formGroupClassName || 'file-input my-2 form-group'}>
{label && (
<label className={labelClassName} htmlFor={name}>
{label}
{isRequired && <span style={{ color: 'red' }}> * </span>}
</label>
)}
<Controller
control={control}
name={name}
rules={rules}
render={(controllerProps) => (
<div
{...getRootProps({
className: 'dropzone w-100 fs-20 d-flex align-items-center',})}
{...controllerProps}
>
<input {...getInputProps()} />
<FontAwesomeIcon
icon={faCloudUploadAlt}
size="sm"
className="mr-1"
/>
{isDragActive ? (
<span className="fs-16">Drop the files here ... </span>
) : (
<span className="fs-16">Select files </span>
)}
</div>
)}
/>
<aside className="thumbs-container">{thumbs}</aside>
{error && <p className="form-error mb-0">{error.message}</p>}
</div>
);
};
,
这是Github讨论中的作品:
export const DropzoneField = ({
name,...rest
}) => {
const { control } = useFormContext()
return (
<Controller
render={({ onChange }) => (
<Dropzone
multiple={multiple}
onChange={e =>
onChange(multiple ? e.target.files : e.target.files[0])
}
{...rest}
/>
)}
name={name}
control={control}
defaultValue=''
/>
)
}
const Dropzone = ({
multiple,onChange,...rest
}) => {
const {
getRootProps,} = useDropzone({
multiple,...rest,})
return (
<div {...getRootProps()}>
<input {...getInputProps({ onChange })} />
</div>
)
}
您应该签出Controller
API,因为它使与外部受控输入的集成更加容易。
https://react-hook-form.com/api#Controller
上面的链接中也有很多示例。
此处参考:https://github.com/react-hook-form/react-hook-form/discussions/2146
,这是 v7 的解决方案
const DropzoneField = ({
name,control,...rest
}: {
name: string;
control: Control<FieldValues>;
}) => {
// const { control } = useFormContext();
return (
<Controller
render={({ field: { onChange } }) => (
<Dropzone onChange={(e: any) => onChange(e.target.files[0])} {...rest} />
)}
name={name}
control={control}
defaultValue=""
/>
);
};
const Dropzone = ({ onChange,...rest }: { onChange: (...event: any[]) => void }) => {
const onDrop = useCallback((acceptedFiles) => {
// Do something with the files
console.log({ acceptedFiles });
},[]);
const { getRootProps,isDragActive } = useDropzone({ onDrop });
return (
<div {...getRootProps()}>
<input {...getInputProps({ onChange })} />
{isDragActive ? (
<p>Drop the files here ...</p>
) : (
<p>Drag 'n' drop some files here,or click to select files</p>
)}
</div>
);
};