搜索

首页  >  问答  >  正文

为什么我无法使用 setState 重置数组?

<p>在我的相机组件中,我想每 3 张照片将照片上传到存储桶。我在 React 中使用状态将图像 blob 保存到数组中。前 3 张照片一切正常,但之后,我无法将数组重置为空,并且看似随机数量的照片被上传到我的存储桶中。</p> <pre class="brush:js;toolbar:false;"> let [blobs, setBlobs] = useState([]); const capturePhoto = async () => { const photo = await camera.current.takePhoto(); fetch(photo.path) .then(res => { setBlobs([...blobs, res._bodyBlob]); console.log('blobs', blobs.length, blobs); }) .catch(err => { console.log('err', err); }); checkLength(); }; const checkLength = async () => { if (blobs.length >= 2) { // upload files to a folder with the current date in a firebase cloud bucket const datestring = new Date().toLocaleString('de-DE'); blobs.forEach((blob, i) => { uploadFile(blob, datestring + '/' + (i + 1) + '.jpg'); }); // reset state setBlobs([]); sendNotification('Photos uploaded'); toggleDialog(); } }; </pre> <p>我通过控制台记录了我的数组,并且大小只会增加。另外,它从零开始控制台日志记录,尽管我已经添加了一个元素,这可能是因为 <code>setState()</code> 是异步的。 我试图通过将其包装在承诺中来等待重置,但遗憾的是这也不起作用。 一旦有 3 个 Blob,如何将其上传到云端并在之后重置列表?</p>
P粉714890053P粉714890053577 天前665

全部回复(2)我来回复

  • P粉439804514

    P粉4398045142023-09-03 16:06:45

    因此,这确实取决于代码的执行方式,特别是 setState 的异步性质,因此您可以使用 setState 的回调形式。这是一个例子:

    1

    setBlobs(prevBlobs => [...prevBlobs, res._bodyBlob]);

    这是包含其余代码的完整示例:

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24

    25

    26

    const capturePhoto = async () => {

      const photo = await camera.current.takePhoto();

      fetch(photo.path)

        .then(res => {

          setBlobs(prevBlobs => [...prevBlobs, res._bodyBlob]);

          console.log('blobs', blobs.length, blobs);

        })

        .catch(err => {

          console.log('err', err);

        });

      checkLength();

    };

     

    const checkLength = async () => {

      if (blobs.length >= 2) {

        // upload files to a folder with the current date in a firebase cloud bucket

        const datestring = new Date().toLocaleString('de-DE');

        blobs.forEach((blob, i) => {

          uploadFile(blob, datestring + '/' + (i + 1) + '.jpg');

        });

        // reset state

        setBlobs([]);

        sendNotification('Photos uploaded');

        toggleDialog();

      }

    };

    回复
    0
  • P粉775723722

    P粉7757237222023-09-03 16:04:09

    看起来像三件事:

    1. 不等待提取调用,在提取完成之前调用 checkLength
    2. 在下一次渲染之前,您不会获得 setState 的新值。这是 React 的基本思想(是否是个好主意还有待商榷),状态值在渲染期间是不可变的。 setState 只是给出下一个渲染将使用的下一个不可变状态。
    3. setState依赖于先前的状态时,您应该将回调传递给setState,而不是直接使用当前值。举个例子,假设你有一个空数组,你调用 fetch 一次,然后在第一个数组完成之前再次调用 fetch 。在执行 ...blob 时,这两个 setState 调用都会引用空数组。通过传递回调,setState 获取作为参数传入的最新值。更多信息: https://react.dev/reference/react/Component#setstate

    最简单的解决方案是将数组作为参数传递给 setState 回调内的 checkLength

    这是问题中的 .then()

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    const capturePhoto = async () => {

      const photo = await camera.current.takePhoto();

      fetch(photo.path)

        .then(res => {

          setBlobs(prev => {

            const newBlobs = [...prev, res._bodyBlob];

            console.log('blobs', newBlobs.length, newBlobs);

            checkLength(newBlobs);

            return newBlobs;

          });

        })

        .catch(err => {

          console.log('err', err);

        });

    };

    这是async await

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    const capturePhoto = async () => {

      const photo = await camera.current.takePhoto();

      const res = await fetch(photo.path).catch(console.error);

      if (!res) return;

      setBlobs(prev => {

        const newBlobs = [...prev, res._bodyBlob];

        console.log('blobs', newBlobs.length, newBlobs);

        checkLength(newBlobs);

        return newBlobs;

      });

    };

    检查长度

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    const checkLength = async (newBlobs) => {

      if (newBlobs.length >= 2) {

        // upload files to a folder with the current date in a firebase cloud bucket

        const datestring = new Date().toLocaleString('de-DE');

        newBlobs.forEach((blob, i) => {

          uploadFile(blob, datestring + '/' + (i + 1) + '.jpg');

        });

        // reset state

        setBlobs([]);

        sendNotification('Photos uploaded');

        toggleDialog();

      }

    };

    回复
    0
  • 取消回复