search

Home  >  Q&A  >  body text

Why can't I reset the array using setState?

<p>In my camera component I want to upload photos to a bucket every 3rd photo. I'm using state in React to save an image blob into an array. Everything works fine for the first 3 photos, but after that, I can't reset the array to empty and a seemingly random number of photos get uploaded to my bucket. </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>I logged my array via console and the size only increased. Also, it starts console logging from scratch even though I've added an element, probably because <code>setState()</code> is asynchronous. I tried to wait for the reset by wrapping it in a promise, but unfortunately that didn't work either. Once there are 3 blobs, how do I upload them to the cloud and reset the list afterwards? </p>
P粉714890053P粉714890053454 days ago582

reply all(2)I'll reply

  • P粉439804514

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

    So it really depends on how the code is executed, specifically the asynchronous nature of setState so you can use the callback form of setState. Here is an example:

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

    Here is the complete example with the rest of the code:

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

    reply
    0
  • P粉775723722

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

    Looks like three things:

    1. Do not wait for the fetch call, call checkLength before the fetch is complete.
    2. You won't get a new value for setState until the next render. This is the basic idea of ​​React (whether it's a good idea is debatable), state values ​​are immutable during rendering. setState Just gives the next immutable state that will be used by the next render.
    3. When setState depends on a previous state, you should pass a callback to setState rather than using the current value directly. For example, let's say you have an empty array, you call fetch once, and then call fetch again before the first array is completed. Both setState calls will reference empty arrays when executing ...blob. By passing a callback, setState gets the latest value passed in as a parameter. More information: https://react.dev/reference/react/Component#setstate

    The simplest solution is to pass the array as a parameter to checkLength inside the setState callback.

    This is .then() in the question:

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

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

    Check length

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

    reply
    0
  • Cancelreply