【问题标题】:How to add a file and its description to the state using dropzone in ReactJs?如何使用 ReactJs 中的 dropzone 将文件及其描述添加到状态?
【发布时间】:2021-01-27 07:10:55
【问题描述】:

如何使用选择、使用 react-dropzone 将一个或多个文件及其描述添加到组件的状态中。

我正在使用 Reactjs、dropzone 和 bootstrap,我想要实现的是:添加一个或多个文件(通过将它们拖动到一个区域),然后查看添加文件的列表和每个文件的选择输入(使用用户定义“类型”的选项)将所有这些保存在一个状态中,然后将该信息发送到 API。 类似于图像中显示的内容:

到目前为止,我的代码根据其扩展名(pdf、xlsx ...)和被拒绝的文件,返回了一个被接受的文件列表,但我不知道如何添加一个选择(带有“type”选项,可以是“summary”、“report”、“test”...)并将其保存为状态,然后将其发送到 API。

我目前使用react-dropzone的代码是这样的:

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

const activeStyle = {
  borderColor: "#f2f"
};

const acceptStyle = {
  borderColor: "#f8f"
};

const rejectStyle = {
  borderColor: "#f2f"
};
function InputFiles(props) {
  const {
    acceptedFiles,
    fileRejections,
    isDragActive,
    isDragAccept,
    isDragReject,
    getRootProps,
    getInputProps
  } = reactDropzone.useDropzone({
    accept: ".xlsx,.docx,.pdf"
  });

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

  const acceptedFileItems = acceptedFiles.map((file) => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
    </li>
  ));

  const fileRejectionItems = fileRejections.map(({ file, errors }) => (
    <li key={file.path}>
      {file.path} - {file.size} bytes
      <ul>
        {errors.map((e) => (
          <li key={e.code}>{e.message}</li>
        ))}
      </ul>
    </li>
  ));

  return (
    <section className="container">
      {/* <div {...getRootProps({ style })}> */}
      <div {...getRootProps({ style })}>
        <input {...getInputProps()} />
        <p>Drag 'n' drop some files here, or click to select files</p>
        <em>(Only *.pdf , *.xlsx , *.docx files will be accepted)</em>
      </div>
      <aside>
        <h4>Accepted files</h4>
        <ul>{acceptedFileItems}</ul>
        <h4>Rejected files</h4>
        <ul>{fileRejectionItems}</ul>
      </aside>
    </section>
  );
}

ReactDOM.render(<InputFiles />, document.body);

window.onload = function() {
 console.log('onload');
   
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.7.2/prop-types.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dropzone/11.2.0/index.js"></script>

我们的目标是得到这样的东西:

添加文件及其描述时,必须以组件的状态保存,目的是点击保存时向API发出POST请求,点击取消时必须删除状态信息

【问题讨论】:

    标签: javascript reactjs file drag-and-drop react-dropzone


    【解决方案1】:

    正如@Emmanuel 在他的回答中提到的那样,您可以将文件存储在一个状态中,这个实现使用名称作为键将文件存储在地图中。

    import React, { useMemo, useState } from "react";
    import { useDropzone } from "react-dropzone";
    
    const baseStyle = {
      flex: 1,
      display: "flex",
      flexDirection: "column",
      alignItems: "center",
      padding: "20px",
      borderWidth: 2,
      borderRadius: 20,
      borderColor: "#26C2E7",
      borderStyle: "dashed",
      backgroundColor: "#fafafa",
      color: "#c4c4c4",
      outline: "none",
      transition: "border .24s ease-in-out"
    };
    
    const activeStyle = {
      borderColor: "#f2f"
    };
    
    const acceptStyle = {
      borderColor: "#f8f"
    };
    
    const rejectStyle = {
      borderColor: "#f2f"
    };
    function InputFiles(props) {
      const [files, setFiles] = useState({});
      const {
        fileRejections,
        isDragActive,
        isDragAccept,
        isDragReject,
        getRootProps,
        getInputProps
      } = useDropzone({
        onDrop: (acceptedFiles) => {
          setFiles((prevFiles) =>
            acceptedFiles.reduce(
              (acc, file) => ({
                ...acc,
                [file.name]: {
                  file,
                  fileType: ""
                }
              }),
              prevFiles
            )
          );
        },
        accept: ".xlsx,.docx,.pdf"
      });
      const style = useMemo(
        () => ({
          ...baseStyle,
          ...(isDragActive ? activeStyle : {}),
          ...(isDragAccept ? acceptStyle : {}),
          ...(isDragReject ? rejectStyle : {})
        }),
        [isDragActive, isDragReject, isDragAccept]
      );
      const acceptedFileItems = Object.keys(files).map((fileName) => {
        const currentFile = files[fileName].file;
        const onSelectChange = (e) => {
          e.persist();
          setFiles((prevFiles) => {
            return {
              ...prevFiles,
              [fileName]: {
                ...prevFiles[fileName],
                fileType: e.target.value
              }
            };
          });
        };
    
        return (
          <li key={fileName}>
            <div style={{ display: "flex" }}>
              <span>
                {currentFile.path} - {currentFile.size} bytes
              </span>
              <select value={currentFile.fileType} onChange={onSelectChange}>
                <option value=""></option>
                <option value="summary">summary</option>
                <option value="description">report</option>
                <option value="report">description</option>
              </select>
            </div>
          </li>
        );
      });
    
      const fileRejectionItems = fileRejections.map(({ file, errors }) => (
        <li key={file.path}>
          {file.path} - {file.size} bytes
          <ul>
            {errors.map((e) => (
              <li key={e.code}>{e.message}</li>
            ))}
          </ul>
        </li>
      ));
    
      return (
        <section className="container">
          {/* <div {...getRootProps({ style })}> */}
          <div {...getRootProps({ style })}>
            <input {...getInputProps()} />
            <p>Drag 'n' drop some files here, or click to select files</p>
            <em>(Only *.pdf , *.xlsx , *.docx files will be accepted)</em>
          </div>
          <aside>
            <h4>Accepted files</h4>
            <ul>{acceptedFileItems}</ul>
            <h4>Rejected files</h4>
            <ul>{fileRejectionItems}</ul>
            <button onClick={() => console.log(files)}>console log files</button>
          </aside>
        </section>
      );
    }
    
    export default InputFiles;
    

    你可以在这里查看工作https://codesandbox.io/s/react-drop-zone-select-vhc2l

    【讨论】:

    • 非常感谢,这正是我想要的。
    【解决方案2】:

    useDropZone 使用回调 onDrop,它可以让您像这样获取文件的名称:

     const onDrop = React.useCallback((acceptedFiles) => {
        // Do something with the files
      }, []);
      const { ... } = useDropzone({ onDrop });
    

    在每次删除文件时获得文件名称后,您可以将其存储在一个状态中,显示名称和选择,并在每次单击选择时相应地更新该状态。

    还要注意doc 说它至少应该是 React 16.8,但您似乎使用的是 React 16.6。

    【讨论】:

    • 您能说得具体一点吗?
    • @diedu 为你做了例子。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-31
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    • 1970-01-01
    • 2017-12-02
    相关资源
    最近更新 更多