【问题标题】:Setting a state variable in react-dropzone `onDrop`, after reading with exceljs在使用 exceljs 阅读后,在 react-dropzone `onDrop` 中设置状态变量
【发布时间】:2020-11-08 02:02:48
【问题描述】:

一个用例是:

  1. 用户删除文件
  2. 获取文件并使用exceljs 读取
  3. 从列中获取值并将其保存在数组中ids
  4. 使用ids 的内容设置状态变量onDropIds。我得到了步骤 1-3 工作。我无法让 4 工作。

请参阅:状态始终打印为空,即使集合包含值。请参见代码行 41-43。

import React, { useState, useCallback } from "react";
import { useDropzone } from "react-dropzone";
import Excel from "exceljs";

export default function Test(props) {
  // Local state
  const [onDropIds, setOnDropIds] = useState(new Set());

  // Callback fires as soon as the file is dropped
  const onDrop = useCallback((acceptedFiles) => {
    const file = acceptedFiles[0];
    const reader = new FileReader(); // reads the file using the `FileReader` API

    reader.onabort = () =>
      console.warn(`Reading of file ${file.path} was aborted.`);
    reader.onerror = () =>
      console.error(`Reading of file ${file.path} has failed.`);

    reader.onloadend = (e) => {
      const bufferArray = reader.result;

      // temporarily hold a set of values
      const ids = new Set();

      const workbook = new Excel.Workbook();
      workbook.xlsx
        .load(bufferArray)
        .then((sheet) => {
          workbook.worksheets[0].eachRow({ includeEmpty: true }, (row) => {
            const colValue1 = (row.values[1] || "").trim().toUpperCase();
            if (colValue1 && colValue1 !== "HEADERNAME") {
              ids.add(colValue1);
            }
          });
        })
        .then(() => {
          // set it to the state
          setOnDropIds(ids);

          // debug: print results
          console.log(onDropIds);
          console.log("---");
          console.log(ids);
        });
    };

    reader.readAsArrayBuffer(file);
  }, []);

  // Initialize the dropzone hook
  const { getRootProps, getInputProps, open, acceptedFiles } = useDropzone({
    noClick: true,
    noKeyboard: true,
    multiple: false,
    onDrop,
  });

  return (
    <div>
      <div
        {...getRootProps()}
        style={
          acceptedFiles && acceptedFiles.length
            ? {
                textAlign: "center",
                border: "1px solid #198562",
                marginTop: "0.5em",
                backgroundColor: "#d9fff3",
              }
            : {
                textAlign: "center",
                border: "1px dashed #000",
                marginTop: "0.5em",
              }
        }
      >
        <input {...getInputProps()} />
        <button
          style={{ marginTop: "0.5em" }}
          color={acceptedFiles && acceptedFiles.length ? "#00ff00" : "#ff0000"}
          onClick={open}
        >
          Browse
        </button>
        {acceptedFiles && acceptedFiles.length ? (
          <span>
            {acceptedFiles[0].path}{" "}
            <i className="fas fa-check-circle" style={{ color: "#75B436" }}></i>
          </span>
        ) : (
          "Drag 'n' drop a file here..."
        )}
      </div>
    </div>
  );
}

【问题讨论】:

  • 我已经用正确的解决方案添加了一个指向我的答案的链接。您可以根据您的要求对其进行检查

标签: reactjs callback react-hooks exceljs react-dropzone


【解决方案1】:

setOnDropIds 是一个异步函数,这意味着 React.js 不会在 setOnDropIds 调用期间立即更改 onDropIds 的值,而是稍后应用更改,在异步组件生命周期计算期间。您需要一个 useEffect 挂钩才能看到更改后的 onDropIds 值。

您可以尝试类似的方法:

const [onDropIds, setOnDropIds] = useState(new Set());

useEffect(() => {
  console.log("onDropIds from effect", onDropIds);
}, [onDropIds]);

及以后:

 .then(() => {
   setOnDropIds(new Set([...onDropIds, ...ids]));
 });

但从您的代码中不清楚为什么需要使用Set 而不是简单的Array,可能只使用数组可能是一个可行的选择:

const [onDropIds, setOnDropIds] = useState([]);

useEffect(() => {
  console.log("onDropIds from effect", onDropIds);
}, [onDropIds]);

稍后:

const ids = [];
const workbook = new Excel.Workbook();

workbook.xlsx
  .load(bufferArray)
  .then((sheet) => {
    workbook.worksheets[0].eachRow({ includeEmpty: true }, (row) => {
      const colValue1 = (row.values[1] || "").trim().toUpperCase();

      if(colValue1 && colValue1 !== "HEADERNAME") ids.push(colValue1);
    });
  })
  .then(() => {
    setOnDropIds([...onDropIds, ...ids]);
  });

最后将onDropIds 转换为您真正需要的Set,使用:

new Set(onDropIds);

【讨论】:

  • 不完全。但我不会试试这个。仅供参考:onDropIds 始终为空,即使在随后的渲染和循环中也是如此。即,我尝试过一个接一个地删除多个文件。似乎无法设置 onDropIds。如果它设置有时,那么我希望这些值至少会填充......
  • 嗨@Kyle,您能否根据您的 cmets 对其他答案检查我的更新答案?
  • 对不起@Kyle,但我不明白为什么在你对我的回答的评论中你说你不能让onDropIds 在 cmets 中设置其他答案你说在useEffect (即按照我的建议)你可以设置它;两个cmets之间有什么变化?您能否使用最新版本的代码更新您的问题?最后(再次从其他 cmets)我了解到 onDropIds 被重置:这可能是由于您的组件在稍后重新安装后被卸载,或者因为 setOnDropIds 是用空的 Set 调用的,没有串联它充当重置.
  • 我的用例是 onDropIds 应该在每个文件放入 dropzone 时设置。我看到它在每一滴上都是空的。不知道发生了什么。我已经使用数组扩展运算符尝试了您的 setOnDropIds。我会标记你的答案和意识到的赏金。
  • 顺便说一句,我可以返回承诺中的值:then(() =&gt; return ids;).then((promisedIds) =&gt; setOnDropIds(prev =&gt; new Set(...prev, ...promisedIds)))
【解决方案2】:

您的代码中有错误,如下所示:

在第 47 行:

reader.readAsArrayBuffer(file);

您必须传递文件才能读取它。

您无法查看的原因:State 始终打印为空,即使 set 包含第 41-43 行的值,因为 setOnDropIds 函数异步设置数据,就像类组件中的 setState 一样。

所以当代码到达第 41 行时,它还没有被设置。

您可以通过在 return 语句之前添加 console.log("onDropIds outside : ", onDropIds); 来检查是否已在 onDropIds 中设置值。

您可以参考以下代码框并检查控制台:

https://codesandbox.io/embed/serene-mclean-tsum3?fontsize=14&hidenavigation=1&theme=dark

可以在https://stackoverflow.com/a/54069332/5599770找到有关 useState 如何异步工作的详细说明

【讨论】:

  • 抱歉,我确实有 file 对象。请参阅编辑...仍然无法正常工作。我认为你正在做一些事情......我可能必须让 FileReader 对象返回一个承诺。
  • @Kyle codesandbox.io/embed/… 你检查过我创建的这个演示吗?它在这里工作正常,并且 onDemoIds 正在控制台上登录
  • @Kyle 设置 id 后你想实现什么?也许这会澄清问题。
  • 其实你是对的。我能够在useEffect 中捕获它。 i.imgur.com/cQtzty1.png 我的代码一直在工作……但奇怪的是,onDropIds 每次都重置为空。 i.imgur.com/VMK111y.png
  • 它重置为空。见扩展。我希望它在随后的文件删除 (2) 中有两个来自 (1) 的条目:i.imgur.com/CfJUrjq.png
【解决方案3】:

setXxx 函数不会立即更新值,它是异步的。您将在下一次重新渲染时看到新值。

如果您需要立即访问组件中的值,您可以使用 refs 代替:

const onDropIds = useRef(new Set());
// onDropIds.current is now new Set() until this is changed elsewhere.

然后在回调中

.then(() => {
  // set it to the ref
  onDropIds.current = ids;

  // debug: print results
  console.log(onDropIds.current);
  console.log("---");
  console.log(ids);
});

编辑:阅读您对其他答案的评论,您似乎需要更新程序功能:

setOnDropIds(prev => new Set([...prev, ...ids]))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    • 1970-01-01
    • 2019-07-31
    • 2016-06-03
    • 2020-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多