Warum kann ich das Array nicht mit setState zurücksetzen?
P粉714890053
P粉714890053 2023-09-02 23:44:06
0
2
587
<p>In meiner Kamerakomponente möchte ich jedes dritte Foto Fotos in einen Bucket hochladen. Ich verwende den Status in React, um einen Bild-Blob in einem Array zu speichern. Bei den ersten drei Fotos funktioniert alles einwandfrei, aber danach kann ich das Array nicht mehr auf leer zurücksetzen und eine scheinbar zufällige Anzahl von Fotos wird in meinen Bucket hochgeladen. </p> <pre class="brush:js;toolbar:false;"> let [blobs, setBlobs] = useState([]); const capturePhoto = async () => const photo = wait 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) { // Dateien in einen Ordner mit dem aktuellen Datum in einem Firebase-Cloud-Bucket hochladen const datestring = new Date().toLocaleString('de-DE'); blobs.forEach((blob, i) => { uploadFile(blob, datestring + '/' + (i + 1) + '.jpg'); }); // Status zurücksetzen setBlobs([]); sendNotification('Fotos hochgeladen'); toggleDialog(); } }; </pre> <p>Ich habe mein Array über die Konsole angemeldet und die Größe hat nur zugenommen. Außerdem wird die Konsolenprotokollierung von Grund auf neu gestartet, obwohl ich ein Element hinzugefügt habe, wahrscheinlich weil <code>setState()</code> asynchron ist. Ich habe versucht, den Reset abzuwarten, indem ich ihn in ein Versprechen verpackt habe, aber leider hat auch das nicht funktioniert. Sobald es drei Blobs gibt, wie lade ich sie in die Cloud hoch und setze die Liste anschließend zurück? </p>
P粉714890053
P粉714890053

Antworte allen(2)
P粉439804514

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

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

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

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();
  }
};
P粉775723722

看起来像三件事:

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

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

这是问题中的 .then()

  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

  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;
    });
  };

检查长度

  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();
    }
  };
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage