使用 react-dropzone 和 inputRef

问题描述

大家好。我是那种喜欢在我的代码中消除警告的程序员,这让我很难过。所以我来描述一下情况。

我正在使用极好的库 react-dropzone用户提供文件拖放库。有时会出现用户可以提供多个文件的情况;其他时候,用户一次只能上传一个。我喜欢用户能够更改要上传文件,我也喜欢他们查看已上传文件。所以我写了一个 TypeScript 文件,它从 dropzone 组件中获取 inputRef,获取文件,甚至将它们作为表格列出。代码有效。

唯一让我烦恼的是它产生了一个我无法消除的警告。看到 onDrop 末尾的 [myFiles,props.multiple] 行了吗?它给了我以下警告:

Line 61:5: React Hook React.useCallback has a missing dependency: 'inputRef'. Either include it or remove the dependency array react-hooks/exhaustive-deps

是的,onDrop 依赖于 inputRef,因为它使用 inputRef 来改变拖放文件列表,但 inputRef ref 变量仅由 useDropzone 创建,它利用 onDrop 作为其参数之一 - 所以我们有一个循环依赖。除了详尽的 deps 警告外,哪个有效。

问题是,如果我将 inputRef 添加[myFiles,props.multiple] 数组,我得到:

Block-scoped variable 'inputRef' used before its declaration.

因为 inputRef 是调用 useDropZone 返回的常量之一。

如果我尝试在 onDrop 声明之前将调用移动到 useDropzone,我会得到(惊奇,惊奇):

Block-scoped variable 'onDrop' used before its declaration.

基本上,onDrop 依赖于由 useDropZone 创建的引用,

那我现在该怎么办?不,我不想让警告静音:它的存在是有原因的。而且 React Hooks 似乎有效,所以我对非 Hooks 解决方案不感兴趣。感谢所有建议。

import React,{ useEffect,useMemo } from "react";
import { Button } from "react-bootstrap";
import { useDropzone } from "react-dropzone";
var path = require("path");

/* 
// This is a drag and drop components integrating ideas from the following
// sources:
//
// 1.How to actually accept a file (and react to it):
// https://codesandBox.io/s/removable-drop-zone-82km9
//
// 2. How to colour it depending on whether files are accepted or rejected:
// https://react-dropzone.js.org/ "Using inline styles"
//
// 3. Getting a ref in there that can be used outside.
// https://github.com/react-dropzone/react-dropzone/issues/838
*/

interface Props {
  setref: any;
  multiple: boolean;
  existfile?: string;
}

function DropZoneComponent(props: Props) {
  const [myFiles,setMyFiles] = React.useState<any>([]);

  const onDrop = React.useCallback(
    (accepted: any,rejected: any,event: any) => {
      if (props.multiple) {
        setMyFiles([...myFiles,...accepted]);
      } else {
        setMyFiles([...accepted]);
      }
      if (event.type === "drop") {
        const dt = new DataTransfer();
        const existfiles = Array.from(inputRef!.current!.files!);
        if (props.multiple) {
          for (let existfile of existfiles) {
            dt.items.add(existfile);
          }
        }
        for (let file of accepted) {
          dt.items.add(file); // Add only file name not matched files
        }
        inputRef!.current!.files! = dt.files; // Overwrite files
      } else {
        const dt = new DataTransfer();
        if (props.multiple) {
          for (let existfile of myFiles) {
            dt.items.add(existfile); // Add only file name not matched files
          }
        }
        for (let existfile of accepted) {
          dt.items.add(existfile); // Add only file name not matched files
        }
        inputRef!.current!.files! = dt.files; // Overwrite files
      }
    },[myFiles,props.multiple]
  );

  const {
    getRootProps,getInputProps,inputRef,isDragActive,isDragAccept,isDragReject,} = useDropzone({
    noKeyboard: true,maxSize: 20971520,multiple: props.multiple,maxFiles: props.multiple ? 0 : 1,accept: "image/*,.pdf",onDrop,});

  const handleRemoveFile = React.useCallback(
    (index) => {
      const dt = new DataTransfer();
      let files = Array.from(inputRef!.current!.files!);
      files.splice(index,1);
      for (let file of files) {
        dt.items.add(file);
      }
      inputRef!.current!.files! = dt.files; // Overwrite files
      setMyFiles(Array.from(dt.files)); // Set states to render file list
    },[inputRef]
  );

  const files = React.useMemo<any>(
    () =>
      myFiles.map((file: any,index: number) => (
        <tr>
          {" "}
          <th key={file.name}>{file.name}</th>
          <td>{file.size}</td>
          <td>
            <Button
              variant="outline-success"
              onClick={() => handleRemoveFile(index)}
            >
              Remove file
            </Button>
          </td>{" "}
        </tr>
      )),[handleRemoveFile,myFiles]
  );

  const baseStyle = {
    flex: 1,display: "flex",alignItems: "center",padding: "20px",borderWidth: 2,borderRadius: 2,borderColor: "#eeeeee",borderStyle: "dashed",backgroundColor: "#fafafa",color: "#bdbdbd",outline: "none",transition: "border .24s ease-in-out",};

  const activeStyle = {
    borderColor: "#2196f3",};

  const acceptStyle = {
    borderColor: "#00e676",};

  const rejectStyle = {
    borderColor: "#ff1744",};

  const style = useMemo(
    () => ({
      ...baseStyle,...(isDragActive ? activeStyle : {}),...(isDragAccept ? acceptStyle : {}),...(isDragReject ? rejectStyle : {}),}),[
      isDragActive,acceptStyle,activeStyle,baseStyle,rejectStyle,]
  );

  useEffect(() => {
    if (inputRef.current !== props.setref.current) {
      props.setref.current = inputRef.current;
    }
  },[props.setref,myFiles]);
  const dropText = !props.multiple
    ? "Drag and drop a file here,or click to select a file."
    : "Drag and drop some files here,or click to select files.";

  let labelText = props.existfile ? (
    <p>
      {dropText}
      <br />
      Existing file: {path.basename(props.existfile)}
    </p>
  ) : (
    <p>{dropText}</p>
  );

  return (
    <section className="container">
      <div {...getRootProps({ className: "dropzone",style: style })}>
        <input {...getInputProps()} />
        {labelText}
      </div>
      {files.length > 0 ? (
        <div>
          <table className="table table-hover plan-table">
            <thead className="plan-table-thead">
              <tr className="plan-table-tr">
                <th className="plan-table-th col">File name</th>
                <th className="plan-table-th col">Length (bytes)</th>
                <th className="plan-table-th col"></th>
              </tr>
            </thead>
            <tbody className="plan-table-tbody">{files}</tbody>
          </table>
        </div>
      ) : (
        ""
      )}
    </section>
  );
}

export default DropZoneComponent;

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)