In my camera component, I want to upload the photos to a storage bucket every 3 photos. I use state in react to save my image blobs to an array. Everything works perfectly for the first 3 photos but afterward, I am not able to reset my array to be empty again and a seemingly random number of photos is uploaded to my bucket.
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();
}
};
I console logged my array and the size only increases. Also, it starts console logging with zero although I already added an element likely because setState()
is asynchronous.
I tried to await the reset by wrapping it in a promise, but that sadly did not work either.
How can I upload the blobs to the cloud once there are 3 of them and reset the list afterward?
2
Answers
Looks like three things:
checkLength
is called before the fetch completes.setState
until the next render. This is a fundamental idea of React (debatable whether it’s a good idea), state values are immutable during a render.setState
just gives the next immutable state that will be used by the next render.setState
depends on the previous state, you should pass a callback tosetState
instead of using the current value directly. As an example, say you have an empty array, you call fetch once, then again before the first one completes. Both of thesesetState
calls would be referencing the empty array when doing...blobs
. By passing a callback,setState
gets the most recent value passed in as a parameter. More info: https://react.dev/reference/react/Component#setstateEasiest solution is to pass the array as a parameter to
checkLength
inside of thesetState
callback.Here’s with
.then()
as in the question:and here’s with
async
await
checkLength
So it does come down to how your code executes, specifically the
asyncronous nature of setState
so what you can do is use thecallback form of setState
. Here is an example:Here’s a complete example with the rest of the code: