0

I would like to view a pdf file from firebase storage in my React app, the problem is I would like to view the file after the page is loaded and the 'getDownloadURL' firebase method is async.

I tried to do something like this:

    const MyPage = (props) => {
      const [numPages, setNumPages] = useState(null)
      const [pageNumber, setPageNumber] = useState(1)
      const [url, setURL] = useState('')
    
     const showFile = async () => {
        return await storage.ref(props.props.sheetMusicRef).getDownloadURL()
      }
    
    return   <Document file={showFile}>
              <Page pageNumber={pageNumber} />
            </Document>
}

with no success.

Peter Haddad
  • 78,874
  • 25
  • 140
  • 134
Nadav Holtzman
  • 210
  • 4
  • 13

1 Answers1

0

I did something similar today, where I wanted to simply fetch a pdf from Firebase Storage, show a loader in the meantime, and render the file. I decided to not go for react-pdf for my own use case, as I wanted to embed the file over the whole viewport. Skip to the next header if you want to see my proposed solution to your code specifically.

My solution

This component fetches the pdf from Firebase Storage, before embedding the file over the whole viewport. For making sure that I do not get the ability to scroll over or under, mimicing the behavior of opening a file straight in the browser, I utilize the "body-scroll-lock"-package from npm. I also use MUI for styled components. So, if you anyone comes onto this post and want a working full screen pdf-viewer, fetching the pdf from Firebase Storage and showing a loader, here you go:

import React from "react";
import { Box, Typography, useMediaQuery } from "@mui/material";
import { FC, useEffect, useState } from "react";
import { useTheme } from "../ThemeProvider";
import { disableBodyScroll, clearAllBodyScrollLocks } from "body-scroll-lock";
import { Seek } from "react-loading-indicators";
import { getDownloadURL, ref } from "firebase/storage";
import { storage } from "../firebaseConfig";

const LandingPage: FC = (props) => {
  const targetRef = React.createRef();
  const [isLoading, setIsLoading] = useState(true);
  const [pdf, setPdf] = useState(null);

  useEffect(() => {
    disableBodyScroll(targetRef);

    getDownloadURL(ref(storage, "firebase_storage_path.pdf"))
      .then((url) => {
        setPdf(url);
        setInterval(() => setIsLoading(false), 500);
      })
      .catch(() => setIsLoading(false));

    return () => {
      clearAllBodyScrollLocks();
    };
  }, []);

  return (
     <Box
     ref={targetRef}
     display="flex"
     justifyContent="center"
     alignItems="center"
     sx={{
       height: "100vh",
       width: "100vw",
     }}
   >
     {isLoading ? (
       <Seek color="#000" size="small" />
     ) : !pdf ? (
       <Typography variant="h4">Oops, something went wrong</Typography>
     ) : (
       <embed type="application/pdf" width="100%" height="100%" src={pdf} />
     )}
   </Box>
  );
};
export default LandingPage;

The solution to your problem specifically

I have modified the code you have provided, and proposed some changes that would work the way you want it to. This shows a loader while fetching the file, fetches the file from Firebase Storage, and shows either an error message if something went wrong, or loads the pdf using react-pdf.

import { useEffect, useState } from "react";
import React from "react";
import { Seek } from "react-loading-indicators";
import { getDownloadURL, ref } from "firebase/storage";
import { storage } from "../firebaseConfig";
import { Document, Page } from "react-pdf";

const MyPage = (props) => {
  const [numPages, setNumPages] = useState(null);
  const [pageNumber, setPageNumber] = useState(1);
  const [isLoading, setIsLoading] = useState(true);
  const [url, setURL] = useState(null);

  useEffect(() => {
    getDownloadURL(ref(storage, "firebase_storage_path.pdf"))
      .then((url) => {
        setURL(url);
        // Could include a way of updating numPages here
        setInterval(() => setIsLoading(false), 500); // Just for the sake of showing the loader
      })
      .catch(() => setIsLoading(false));

    return () => {};
  }, []);

  if (isLoading) return <Seek color="#000" size="small" />; // Show loader
  else {
    if (!url)
      return <p>Oops, something went wrong</p>; // If catched error from fetch
    else
      return (
        <Document file={url}>
          <Page pageNumber={pageNumber} />
        </Document>
        // Could also include a way of switching page number based on numPages
      );
  }
};
export default MyPage;
Martin
  • 66
  • 3