【问题标题】:Array is sometimes recreated, and in other times modified数组有时会重新创建,有时会修改
【发布时间】:2020-04-26 21:42:19
【问题描述】:

我正在尝试在我的 React 应用中实现多图像文件选择器。

这是我的输入元素:

<input
type="file"
multiple
onChange={handleImageChange}
/>

{renderPhotos(venueImages)}

这些是选择文件时调用的函数:

  const [venueImages, setVenueImages] = useState([]);`
  const renderPhotos = source => {
    console.log(source); ////////log 1

    return source.map(photo => {
      return <img src={photo} key={photo} />;
    });
  };

  const handleImageChange = e => {
    if (e.target.files) {
      const filesArray = Array.from(e.target.files);
      console.log(filesArray); ///////// log 2
      filesArray.forEach(file => {
        const tempUrl = URL.createObjectURL(file);
        console.log(tempUrl); ////// log 3

        setVenueImages([...venueImages, tempUrl]);
      });
    }
  };

我打电话给renderPhotos,在上传之前显示所有选定照片的预览。

我面临的问题如下: 例如,如果我选择 5 张照片,最终只会在屏幕上呈现 1 张。 我在handleImageChange 中插入了控制台日志,而我所记录的内容让我更加困惑。 第二个日志(我在我的代码中对它们进行了编号)打印了一个包含 5 个文件的数组。 从日志 3 之后,我将获得每个文件新生成的临时 URL 的 5 个日志。

但日志 1 只会打印一次。

现在 - 如果我单击输入元素以选择更多文件,我将得到另一个渲染图像。 所以基本上每次我选择图像时,无论我选择了多少,我只会再渲染一张图像。

【问题讨论】:

  • 为什么不只调用一次 setVenueImages 而不是为每个图像调用一次呢?你看到哪张图片,第一个加载的?最后一个?
  • @grokked 我看到了最后一个。如果想使用一次,我该怎么做?我不需要创建一个新数组,然后将每个新生成的 url 推送到它,然后调用我的 setVenueImages 命令吗?那不是不必要的代码吗?我现在就试试。
  • 好吧!那行得通:)您可以将其添加为答案,我会批准它。但是我还是不明白,为什么先推入一个数组,然后用那个数组设置我的场地,这和轮流推每个项目有什么不同?

标签: javascript reactjs file react-hooks


【解决方案1】:

问题是您在 setVenueImages 调用中引用了 venueImages 数组。因为setVenueImages 执行的状态更新是异步的,所以你不能确定venueImages 包含所有之前的状态更新。

设置状态函数可以传递一个接受旧值的函数,而不仅仅是传递新值。这将确保您的状态更新是连续的。将您的 setVenueImages 电话替换为:

setVenueImages(prevImages =&gt; [...prevImages, tempUrl]);

我建议的另一个更改是对所有图像执行串联,而不是逐个添加它们。这应该更快。

const handleImageChange = e => {
  if (e.target.files) {
    const filesArray = Array.from(e.target.files).map(file => URL.createObjectURL(file));
    console.log(filesArray); ///////// log 2
    setVenueImages(prevImages => prevImages.concat(filesArray));
  }
};

【讨论】:

  • 谢谢你的作品!我尝试了您的第二个建议,但是当我没有生成临时 URL 时,预览无法正确显示(它不会加载图像,只会显示默认图像“错误”占位符)。我错过了什么吗?还是该方法只是在上传之前将文件加载到数组中?
  • @Tsabary 啊,是的,抱歉:我错过了您创建 URL 的部分。更新了我的答案。
  • 我知道这不是来自您的,但我喜欢您的解决方案。但是如何避免使用 createObjectURL 的内存泄漏?你不需要添加revokeObjectURL() 来从内存中的某个地方删除旧的图像 URL 吗? reference
  • @zipzit 我认为不同之处在于这里 URL.createObjectURL 仅在将新文件添加到状态之前调用它们,而不是在每次状态更新时为新状态中的每个项目调用.由于每个上传的文件只创建一个对象 URL,因此这种方法不会导致内存泄漏。当然,如果不再需要对象 URL,稍后撤销它是很重要的。
【解决方案2】:

这是因为当您保存tempUrl 时,只会保存一个网址。也不要通过一张一张添加图片来设置状态。

你的handleImageChange函数的更新版本可以

const handleImageChange = e => {
    if (e.target.files) {
      const filesArray = Array.from(e.target.files);
      const tempUrls = filesArray.map(file => URL.createObjectURL(file)))
      setVenueImages([...venueImages, ...tempUrls])
    }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-03-11
    • 1970-01-01
    • 2019-02-11
    • 1970-01-01
    • 2013-09-04
    • 1970-01-01
    • 2015-06-10
    • 2017-05-15
    相关资源
    最近更新 更多