Home > Back-end >  Listening to async upload task to handle loading state
Listening to async upload task to handle loading state

Time:07-27

I'm trying to set a loading state according to the state of sending. If the image is sent I will simply insert a loading indicator. Although whenever the task is running, I'm receiving a done (.then()) before the task actually finishes, and the loading indeicator doesnt work as expected:
The task:

export async function storeImage(uri: string, description: string) {
  const reference = ref(storage, `posts/${auth.currentUser!.uid}/${uuid.v4()}`);

  const blob: any = await new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.onload = function () {
      resolve(xhr.response);
    };
    xhr.onerror = function (e) {
      console.log(e);
      reject(new TypeError('Network request failed'));
    };
    xhr.responseType = 'blob';
    xhr.open('GET', uri, true);
    xhr.send(null);
  });

  const uploadTask: any = uploadBytesResumable(reference, blob);

  uploadTask.on(
    'state_changed',
    (snapshot: any) => {
      const progress = snapshot.bytesTransferred / snapshot.totalBytes;
      console.log('Upload is '   progress   '% done');
      switch (snapshot.state) {
        case 'paused':
          // console.log('Upload is paused');
          break;
        case 'running':
          console.log('Upload is running');
          break;
      }
    },
    (error: any) => {
      // Handle unsuccessful uploads
      console.log('Storage error', error);
      blob.close();
    },
    () => {
      getDownloadURL(uploadTask.snapshot.ref).then(async (downloadURL) => {
        console.log('File available at', downloadURL);
        await addImageReferenceToFirestore({ url: downloadURL, description });
      });

      blob.close();
    }
  );
}

if I insert an await on const uploadTask: any = uploadBytesResumable(reference, blob); I get this error, and it stays loading forever:

[Unhandled promise rejection: TypeError: undefined is not a function (near '...uploadTask.on...')]

This is my component:

export function Post({ navigation, ...rest }: any) {
  const [description, setDescription] = useState('');
  const [loading, setLoading] = useState(false);

  async function createPost() {
    setLoading(true);
    await storeImage(rest.route.params.images[0], description).then(() => console.log('done'));
    setLoading(false);
  }

  return (
    <Container>
      {loading ? (
        <Circle size={RFValue(25)} indeterminate={true} color="red" />
      ) : (
        <CreateBar
          text={i18n.t('createPost.title')}
          iconName="CreatePost"
          onPressIcon1={() => navigation.goBack()}
          onPressIcon2={() => createPost()}
        />
      )}
      <ContainerDescription>
        <ImageContainer>
          <Image
            source={{ uri: rest.route.params.images[0] }}
            style={{ width: '100%', height: '100%', borderRadius: 8 }}
          />
        </ImageContainer>

        <TextAreaContainer>
          <InputText
            placeholder={i18n.t('createPost.describe')}
            onChangeText={(newText: string) => setDescription(newText)}
          />
        </TextAreaContainer>
      </ContainerDescription>

      <LightDivider />

CodePudding user response:

You are receiving a done from the console.log before the task finishes, because you're not actually awaiting for the task to finish. You are just awaiting the blob part.

If you just want to get to the final result of the uploaded image URL, without displaying any progress, then you should also await for the uploadTask.on to finish. Wrap uploadTask.on inside a Promise, resolve on successfully uploading the image and reject on errors:

await new Promise((resolve, reject) => {
  uploadTask.on(
    'state_changed',
    (snapshot: any) => {
      const progress = snapshot.bytesTransferred / snapshot.totalBytes;
      console.log('Upload is '   progress   '% done');
      switch (snapshot.state) {
        case 'paused':
          // console.log('Upload is paused');
          break;
        case 'running':
          console.log('Upload is running');
          break;
      }
    },
    (error: any) => {
      // Handle unsuccessful uploads
      console.log('Storage error', error);
      blob.close();
      reject(error);
    },
    () => {
      getDownloadURL(uploadTask.snapshot.ref)
        .then(async (downloadURL) => {
          console.log('File available at', downloadURL);
          try {
            await addImageReferenceToFirestore({ url: downloadURL, description });
          } catch (err) {
            reject(err);
          }

          resolve();
        })
        .catch((err) => reject(err));

      blob.close();
    },
  );
});

And you should also add a try/catch/finally block to your createPost function. Otherwise if the Promise is rejected, the setLoading(false) would never be executed:

async function createPost() {
  try {
    setLoading(true);
    await storeImage(rest.route.params.images[0], description);
  } catch (err) {
    console.error(err);
  } finally {
    setLoading(false);
  }
}
  • Related