如何使用带有react-hook-form的react-dropzone?

问题描述

如何使用带有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>
  );
};