Home > Enterprise >  Custom Hook to Load Firebase Image URL
Custom Hook to Load Firebase Image URL

Time:04-20

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;

  • Related