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