【问题标题】:How to use react-dropzone with react-hook-form?如何将 react-dropzone 与 react-hook-form 一起使用?
【发布时间】:2020-11-11 09:38:11
【问题描述】:

如何使用react-dropzonereact-hook-form 以使表单返回正确的文件 - 不仅仅是文件名

【问题讨论】:

标签: reactjs react-hooks react-hook-form react-dropzone


【解决方案1】:

这是a react-hook-form Github discussion的作品:

export const DropzoneField = ({
  name,
  multiple,
  ...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,
    getInputProps,
  } = useDropzone({
    multiple,
    ...rest,
  })

  return (
    <div {...getRootProps()}>
      <input {...getInputProps({ onChange })} />
    </div>
  )
}

您应该查看Controller API,因为它使与外部控制输入的集成更容易。里面的例子也不少。

【讨论】:

【解决方案2】:

我是这样做的@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, setValue, setError, 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,
    accept,
    onDrop,
    minSize: 0,
    maxSize,
    multiple,
  });
  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>
  );
};

【讨论】:

    【解决方案3】:

    这里是 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, getInputProps, 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>
      );
    };
    

    【讨论】:

    • 您如何通过提交表单且未添加文件来管理 dropzone 本身的错误?
    猜你喜欢
    • 1970-01-01
    • 2020-11-23
    • 2022-06-12
    • 2022-11-22
    • 2022-10-13
    • 1970-01-01
    • 2020-04-04
    • 2022-12-21
    • 2021-05-28
    相关资源
    最近更新 更多