【问题标题】:Updating the state on every iteration of for loop在 for 循环的每次迭代中更新状态
【发布时间】:2020-02-27 01:00:04
【问题描述】:

伙计们! 在进行for-loop 的每次迭代时我遇到了一个问题,我的数组变量在状态下仍然是 1 个元素。这是我的输入
<input accept="image/*" type="file" onChange={handlePhotoChange} multiple/> 这就是handlePhotoChange 方法

const [images, setImages] = useState([])
const handlePhotoChange = async (event) => {
    const { files } = event.target;

    for (let i = 0; i < files.length; i += 1) {
      const file = files[i];

      if (!file.type.match('image')) {
        continue;
      }

      setImages([...images, { loading: true, src: null }]);
      await firebase.doUploadImage(file, auth.uid);
      const url = await firebase.getImageUrl(file.name, auth.uid);
      setImages([...images.slice(0, -1), { loading: false, src: url }]);
    }
}

我在这里所做的是在将图像上传到 Firebase 存储之前将加载变量设置为 true,并通过 loading 我显示一个加载器。然后我的上传准备好了,我得到了图像的 firebase url 并覆盖了数组 images[] 中的最后一个元素。然后我们继续下一张图片。这样,我将在每个图像上显示一个加载器。但问题是循环后的images 数组只有最后添加的元素。我想这与 setImages 函数有关,它是异步的,但正在等待您的意见。谢谢你:)

【问题讨论】:

  • 您误解了useState 的工作原理,images 将始终具有与循环启动时相同的值。

标签: javascript reactjs firebase firebase-realtime-database firebase-storage


【解决方案1】:

它是异步的,只改变一次状态。另外,如果你想访问之前的状态,你应该使用 useState 的回调版本

const [stateObject, setObjectState] = useState({
  firstKey: '',
  secondKey: '',
});

setObjectState((prevState) => ({
  ...prevState,
  secondKey: 'value',
}));

您可以将加载状态和上传状态分开,以便于管理,伪代码如下

const handlePhotoChange = (event) => {
  const { files } = event.target;
  const loadingImages = [];
  for (let i = 0; i < files.length; i += 1) {
    const file = files[i];

    if (!file.type.match('image')) {
      continue;
    }
    loadingImages.push({ file });

    firebase.doUploadImage(file, auth.uid)
      .then(// add to uploaded images state)
  }
  // set loading images state after iteration is complete
}

【讨论】:

  • 但在你的例子中我不会有loading。在我的示例中,我在使用 firebase 处理图像之前添加了loading: true。我想要的是:1)为第一张图片添加loading: true=>显示加载器动画; 2)在firebase中处理图像 3)在firebase回调准备好后添加状态中的第一个图像url并设置loading: false所以加载器动画停止并显示图像。 4) 继续下一张图片,依此类推...
  • 您没有将ids 用于您的文件,因此您是否将加载跟踪为数组大小的计数或仅计算数组中有多少对象具有loading: true 值并不重要
  • 好的,考虑到我正在使用{ index: i, loading: true, src: null }
【解决方案2】:

如果我正确理解您的问题,这样的解决方案是否可行:

const [images, setImages] = useState([]);

const handlePhotoChange = async (event) => {
    const { files } = event.target;

    setImages([...images, { loading: true, src: null }]);

    let newImagesArray = [];

    for (let i = 0; i < files.length; i += 1) {
      const file = files[i];

      if (!file.type.match('image')) {
        continue;
      }

      await firebase.doUploadImage(file, auth.uid);
      const url = await firebase.getImageUrl(file.name, auth.uid);
      let obj = {loading: false, src: url};
      newImagesArray.push(obj);
    }

    //get state
    let newImages = images;
    //add new images
    newImages.push(newImagesArray)
    //update state
    setImages(newImages);
}

我们正在做的是创建一个临时数组newImages 来保存所有新文件。然后,一旦我们遍历所有上传的文件,我们就将数组放入状态。

让我知道它是否有效/是否有问题。

【讨论】:

  • 最后不是setImages([...images, ...newImages]);吗?
  • @KristianKamenov 我本可以搞砸那部分,抱歉。我更新了代码以显示我通常如何处理更新数组状态。
  • 在您的示例中,我将首先推入状态{ loading: true, src: null },然后对于循环的每次迭代,我将推入{ loading: false, src: url },但事实并非如此。情况是当我首先迭代时,我推送{ loading: true, src: null },然后我在firebase中处理图像,同时我向用户展示加载器动画。然后当图像被处理时,我推送{ loading: false, src: url } 并停止加载动画并显示上传的图像。然后我继续下一个图像和下一个图像等等......
  • @KristianKamenov 你能告诉我你的images 状态在有数据时是什么样子吗?
  • 遇到这个问题,我在数组中只有 1 个值,它是 [{ loading: false, src: {last_processed_image_ID}}]
猜你喜欢
  • 1970-01-01
  • 2020-08-07
  • 1970-01-01
  • 2018-02-13
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
  • 2012-08-07
  • 1970-01-01
相关资源
最近更新 更多