I'm trying to create a custom hook that will load the URL for an image stored in Firebase.
So far I've arrived at the following:
useLoadImageURL.js
export default async function useLoadImageURL(projectId, fileName) {
const [url, setURL] = useState()
useEffect(() => {
return url
}, [url])
const storage = getStorage(app);
setURL(await getDownloadURL(ref(storage, `images/projects/${projectId}/${fileName}`)))
}
However, when I use it in my component, I only get promises, and therefore using the returned promise as URL does not work.
I'm calling the hook via the following:
ProjectCard.js
export default function ProjectCard({ project }) {
const coverImageURL = useLoadImageURL(project.id, project.coverImagePath)
return <img src={coverImageURL} />
}
How can I change the hook or how I'm calling it to retrieve the actual URL?
CodePudding user response:
You can remove async
and await
from your code, and instead of calling setUrl
at the bottom of your hook -- which would initiate the call every time the callee component rerenders -- wrap it in a useEffect
, and update the state in a then
callback.
edit: Full code:
const storage = getStorage(app); // as app seems to be external to the hook, storage can likely be too
export default function useLoadImageURL(projectId, fileName) {
const [url, setUrl] = useState();
useEffect(() => {
getDownloadURL(ref(storage, `images/projects/${projectId}/${fileName}`))
.then((value) => {
setUrl(value); // should work provided getDownloadURL actually is a promise that gives a string
});
}, [projectId,fileName]);
return url;
}
This will make the url
state only change when projectId
or fileName
changes.
CodePudding user response:
You have to do your side effect logic (awaiting async response in your case) inside the useEffect
hook.
Following your intent to do it with promises and async
await
, your snippet hook should be something like this.
import { useState, useEffect } from "react";
// hook
function useLoadImageURL(filename) {
const [url, setURL] = useState("some-image-spinner-url");
// simulating your async api call to firebase service
const setTimeoutAsync = (cb, delay) =>
new Promise((resolve) => {
setTimeout(() => {
resolve(cb());
}, delay);
});
useEffect(() => {
async function asyncFn() {
const url = await setTimeoutAsync(() => filename, 3000);
setURL(url);
}
asyncFn();
}, [filename]);
return [url];
}
// main component
function App() {
const [url] = useLoadImageURL("firebase-file-name-f1");
return <div> {url} </div>;
}
export default App;