Unhandled promise rejection: undefined is not a function on Firebase reference - reactjs

I am getting unhandled promise rejection maybe because of ref is not passing please review this if possible. I'll be thankful to you.
[Unhandled promise rejection: TypeError: undefined is not a function (near '...ref.on...')]
In upload file code is distributed based on their functionality:
import firebase from '../../firebase'
const getLocalPath = async (uri)=>{
const response = await fetch(uri);
const blob = await response.blob();
return blob
}
const upload = async (blob, className,acc)=>{
const ref = await firebase.storage().ref(`/${className}`).child(`/${acc}`).put(blob)
console.log('trigger')
console.log('ref', ref)
return ref
};
const getLink = async (ref, className, acc)=>{
await ref.on(
"state_changed",
(snapshot) => {},
(err) => {
console.log(err);
},
() => {
storage
.ref(`/${className}`)
.child(`/${acc}`)
.getDownloadURL()
.then(async (url) => {
// setURL(url)
console.log(url)
return url
});
}
);
}
export {getLink, getLocalPath}
export default upload
This is where I use it:
const Publish= async ()=>{
setLoading(true)
console.log(id)
var count = 1
const blob = await getLocalPath(images[0])
const ref = await upload(blob, `serviceImages/${id}`, `${count}`, setURL)
const uri = await getLink(ref, `serviceImages/${id}`, `${count}` )
console.log(uri)
setLoading(false)
}

firebase.storage.Reference#put returns a firebase.storage.UploadTask. This UploadTask class does have a UploadTask#on() method, BUT, because you used await, you actually get a UploadTaskSnapshot object instead which is the result of UploadTask#then().
So to return the UploadTask instead of the UploadTaskSnapshot, omit the await here.
const upload = (blob, className, acc) => {
return firebase.storage()
.ref(`/${className}`)
.child(`/${acc}`)
.put(blob)
};
But if you want to wait for the upload, just await the UploadTask when you consume the code.
const blob = await getLocalPath(images[0])
const uploadTask = upload(blob, `serviceImages/${id}`, `${count}`, setURL); // note no await here
const snapshot = await uploadTask; // wait for upload to finish
const uri = await getLink(uploadTask, `serviceImages/${id}`, `${count}`)
You could also do this:
const blob = await getLocalPath(images[0])
const uploadTask = upload(blob, `serviceImages/${id}`, `${count}`, setURL);
const [snapshot, uri] = await Promise.all([
uploadTask,
getLink(uploadTask, `serviceImages/${id}`, `${count}`)
]);
You can also simplify getLink to just:
const getLink = async (uploadTask) => {
return uploadTask
.then((snapshot) => snapshot.ref.getDownloadURL());
}
which leads to:
const blob = await getLocalPath(images[0])
const uploadTask = upload(blob, `serviceImages/${id}`, `${count}`, setURL);
const [snapshot, uri] = await Promise.all([
uploadTask,
getLink(uploadTask)
]);
As a side note, try to avoid using await if you are just going to return the value on the next line. It just adds an unnecessary step as you can see here:
const getLocalPath = async (uri)=>{
const response = await fetch(uri);
const blob = await response.blob();
return blob
}
effectively becomes
const getLocalPath = async (uri) => {
return fetch(uri)
.then(response => response.blob())
.then(blob => blob); // <- this line does nothing interesting
}
So just write it as this instead:
const getLocalPath = async (uri) => {
const response = await fetch(uri);
return response.blob();
}

Related

Object (promise.all) to useState

I am trying to set an object to useSate.
The object is the dataMap which should be passed to the setResults
Any help would be appreciated ?
export default function Home() {
const [results, setResults] = useState([]);
const fetchResults = async () => {
const res = await fetch(
'someurl'
);
const data = await res.json();
const dataMap = data.reduce((acc, curr) => {
return {
...acc,
[curr.id]: curr
};
}, {});
const requests = Object.keys(dataMap).map(async (productId) => {
const request = await fetch(
`someUrl/${productId}`
);
const response = await request.json();
return response;
});
const responseAll = await Promise.all(requests);
responseAll.forEach(
({ id, color }) => (dataMap[id] = { ...dataMap[id], color })
);
//console.log(dataMap);
setResults(dataMap)
};
const requests = Object.keys(dataMap).map(async (productId) => {
const request = await fetch(
`someUrl/${productId}`
);
const response = await request.json();
return response;
});
This piece of code will trigger all the api's till the end of Object.keys(dataMap)
To update the state with the values.
You need to update the state just after await like this:
const requests = Object.keys(dataMap).map(async (productId) => {
const request = await fetch(
`someUrl/${productId}`
);
const response = await request.json();
setResults(prev=>[...prev,response])
});

NextJS SWR multiple API calls returnin undefined

So when I try to fetch data from API endpoints with SWR library I'm able to only get the first fetcher function to return data... All API endpoints are working fine if I check them in browser but in code data1,2,3,4 all return undefined if I console log them, am I not using this library correctly to fetch multiple APIs?
const fetcher = async () => {
const response = await fetch("API");
const data = await response.json();
return data;
};
const fetcher1 = async () => {
const response1 = await fetch("API");
const data1 = await response1.json();
return data1;
};
const fetcher2 = async () => {
const response2 = await fetch("API");
const data2 = await response2.json();
return data2;
};
const fetcher3 = async () => {
const response3 = await fetch("API");
const data3 = await response3.json();
return data3;
};
const fetcher4 = async () => {
const response4 = await fetch("API");
const data4 = await response4.json();
return data4;
};
export default function Dashboard(props) {
const {data, error} = useSWR("name1", fetcher);
const {data1, error1} = useSWR("name2", fetcher1);
const {data2, error2} = useSWR("name3", fetcher2);
const {data3, error3} = useSWR("name4", fetcher3);
const {data4, error4} = useSWR("name5", fetcher4);
if (error) return "Error";
if (!data) return "Loading";
console.log(data);
return (
<div>{data.name}</div>
)
Note that useSWR always return data as data, not data1 or data2.
You are destructing object in a wrong way. In fact it should be done like
const { data: data1, error: error1 } = useSWR('name2', fetcher1)

React Native wait for async func

I wanna add multiple photo to db by Array.map() and after that add Array with url storage to collection.
I have problem with async function, because i should wait for this function await addImages() but something is not good.
const addImages = async () => {
image.map(async (imagePhoto) => {
const childPath = `post/${firebase.auth().currentUser.uid}/${Math.random().toString(36)}`;
const response = await fetch(imagePhoto);
const blob = await response.blob();
const task = firebase
.storage()
.ref()
.child(childPath)
.put(blob);
const taskProgress = snapshot => {
console.log(`transferred: ${snapshot.bytesTransferred}`)
}
const taskCompleted = () => {
task.snapshot.ref.getDownloadURL().then((snapshot) => {
imageDB.push(snapshot)
})
}
const taskError = snapshot => {
console.log(snapshot)
}
task.on("state_changed", taskProgress, taskError, taskCompleted);
})
}
const addToDbServices = async () => {
await addImages();
firebase.firestore().collection("services")
.doc(firebase.auth().currentUser.uid)
.collection("userServices")
.add({
nameService,
errorCode,
description,
imageDB,
status,
creation: firebase.firestore.FieldValue.serverTimestamp()
}).then(() => {
Alert.alert('Serwis', 'Twoje zgłoszenie zostało pomyślnie dodane'),
navigation.goBack()
})
}
image.map(async (imagePhoto) => {...})
This creates an array of promises. These are executed but not awaited by default, so code execution continues regardless whether the operations are finished or not. If you want to await all these promises you can use Promis.all() like that:
const addImages = async () => {
const pendingOperations = image.map(async (imagePhoto) => {...});
// wait until all images are processed
return Promise.all(pendingOperations); // or await Promise.all(pendingOperations);
}
const addToDbServices = async () => {
await addImages();
...
}

Firebase storage getImageURL after uploading to after save URL in firestore(React)?

I just want to upload an image then get is URL to save to firestore because i want to save the url of that image to an object. I just want the await to wait for the upload to be finished and then to get the url.
Problem is when i try to get the url it says it doesnt exist but when i go to firebase is there.
const fileData = await fileUpload(imageHome, values.newHomeTeamName);
const url = await storage.ref(fileData).getDownloadURL();
console.log(url);
const fileUpload = async (image: File, newHomeTeamName: string) => {
const fileName = formatFileName(image.name, newHomeTeamName);
const uploadTask = storage.ref(fileName).put(image);
await uploadTask.on(
'state_changed',
snapsphot => {},
error => {
console.log(error);
}
);
return fileName;
};
Your fileUpload function looks a bit unusual to me. You're using await on the on() call, but that doesn't return a promise. What you should do instead is wait on the task itself. So something like:
const fileUpload = async (image: File, newHomeTeamName: string) => {
const fileName = formatFileName(image.name, newHomeTeamName);
const uploadTask = storage.ref(fileName).put(image);
await uploadTask;
return fileName;
}
Or a bit simpler:
const fileUpload = async (image: File, newHomeTeamName: string) => {
const fileName = formatFileName(image.name, newHomeTeamName);
await storage.ref(fileName).put(image);
return fileName;
}
If you want to handle the error, you can catch it in there too. But since all you do is log it, I'd recommend letting it escape and leave it to the runtime to log it.
This is what i did for my app and the url does get stored in the firestore
//imports
import { storage, db } from './firebase'
import firebase from 'firebase'
//states or hooks
const [caption, setCaption] = useState('')
const [image, setImage] = useState(null)
const [progress, setProgress] = useState(0)
const handleUpload=()=>{
const uploadTask = storage.ref(`images/${image.name}`).put(image);
uploadTask.on(
'state_changed',
(snapshot)=>{
const progress = Math.round(
(snapshot.bytesTransferred / snapshot.totalBytes) *100
);
setProgress(progress)
},
(error)=>{
console.log(error);
alert(error.message);
},
()=>{
storage
.ref('images')
.child(image.name)
.getDownloadURL()
.then(url =>{
db.collection('posts').add({
timestamp: firebase.firestore.FieldValue.serverTimestamp(),
caption: caption,
imageUrl: url,
username: uploadname
});
setImage(null);
setProgress(0);
setCaption("");
})
}
)
}
The handleUpload trigress off when the upload button is clicked

How to get URL from firebase storage?

I have the following code:
const imgURL = () => {
const fileName = report.reporter.avatar.Pc.path.segments[8];
const storageRef = storage.ref('images');
storageRef.child(`/${fileName}`).getDownloadURL().then((url) => {
console.log(url) // here I am getting the url in the console
return url;
});
}
console.log(imgURL()); // here its undefiend
for some how I dont know why I cant get what the function return
You can try something like this with async / await i don't know if async / await works for react.
const imgURL = async () => {
const fileName = report.reporter.avatar.Pc.path.segments[8];
const storageRef = storage.ref('images');
const url = await storageRef.child(`/${fileName}`).getDownloadURL().catch((error) => { throw error });;
return url;
}
change your calling method with this :
console.log(await imgURL());
that what fixed my code (useState)
const [avatarUrl, setAvatarUrl] = useState('');
const imgURL = async () => {
const fileName = report.reporter.avatar.Pc.path.segments[8];
const storageRef = storage.ref('images');
await storageRef.child(`/${fileName}`).getDownloadURL().then((url) => {
setAvatarUrl(url);
});
}
imgURL();

Resources