Search code examples
javascripthtmlreactjsredux

First fetch and then display PDF in separate browser without popup warning


I am currently faced with the following problem:

A single button click is supposed to fetch base64-encoded data for a PDF from an API, and the document is subsequently supposed to be displayed in another browser tab.

I use React in combination with a Redux store as frontend framework.

Here is the basic setup:

We have a button. On click, a GET request to the backend API is triggered:

<button onClick={() => fetchDocument(id)}/>

Providing the API with a document id, it successfully returns the base64-encoded data of the PDF as well as the media type (application/pdf). The relevant data (base64 string and application type) is stored in a redux store object, let's call it pdfDocData.

I then use React's useEffect hook to basically check if that specific redux store variable has changed and if it is defined. If so, I initiate the opening of the new tab like so:

useEffeft(() => {
  if (reduxStore.pdfDocData) {
    const docData = reduxStore.pdfDocData;
    const blob = base64ToBlob((docData.base64Data), docData.type);
    const url = URL.createObjectURL(blob);
    const pdfWindow = window.open();
    pdfWindow.location.href = url;
  }
}, [reduxStore.pdfDocData]);

So, upon button click, first, the data is fetched from the backend api, the response is then stored in the redux store. And by subscribing to that store and "listening" for updates via useEffect, I initiale displaying the PDF in a new Browser tab.

And this works absolutely fine when I try this out with the Google Chrome browser.

However, e.g., Firefox and Safari, are more strict here. They basically regard this as a pop-up and, by default, block the attempt to open a new window.

I tried a lot of things in order to make all browsers just display the pdf in a new tab without hesitation. I played around with the arguments inside window.open() e.g.,

window.open(url, "_blank", "norefferer, noopener")

Then I tried using the <iframe> or <embed> tags, but none of these worked. The problem always remained the same.

As far as I have understood, this basically happens because the button click event and the opening of the new browser tab are not, how can I say..., "directly related". We always first have the fetching process, the redux store update, which in turn triggers the opening of the new browser tab.

I currently don't see any chance or way how I can achieve my goal, namely fetching the pdf data and then opening it in a new tab without interruption. It actually sounds like a standard task, fetching pdf data and subsequently displaying it. But I don't see how I can avoid certain browsers from blocking the pop-up.

I would be very happy about any hint or suggestion how I could overcome that issue.

Thanks very much in advance.


Solution

  • Actually found the answer.

    The trick is to open a new, blank window right after the button click.

    const pdfWindow = window.open('about:blank');
    

    Then do all the asynchronous data fetching stuff inside a try-catch block. And after you received the response, you create a URL and forward it to the already opened window like so:

            try {
            
                const res: any = await fetch("https://www.giveMeSomePdfData.org/now");
    
                const blob = base64ToBlob(res.payload);  /// just some function to make a blob out of base64 data
                const url = URL.createObjectURL(blob);
                pdfWindow.location.href = url; // and here, we finally forward the data to the new window
                pdfWindow.focus();
            } catch (e) {
                pdfWindow.close();
                const error = new Error(e)
                console.log(error)
            }