title: React/Node.js - API Call to Generate then Display PDF
body: <p>I'm having trouble with displaying dynamically generated PDFs on a React frontend. I'm able to generate and make the PDF available for download, but the creation/placement of it into my client-side directory is causing issues where the updated state of the app is repeatedly refreshing. The app has users write a text input, analyzes it and pulls up a list of possible images from an API based on the text, then lets the user select which images they want and packages them into a pdf for downloading.</p>
  
  <p>I'm working in a MERN stack with the create-react-app boilerplate serving a frontend application nested inside of a Node.js API directory. For the PDFs I'm using <code>pdfkit</code> to generate the PDFs on the backend and <code>react-pdf</code> to render them on the frontend. My file structure looks like this:</p>
  
  <pre><code>client
    --public
      --pdfs
        --&lt;generated pdf saved here&gt;
    --src
      --components
        --Root.js
        --ImageSelect.js
        --ImageDownload.js
        --PDFDisplay.js
  controller
    --&lt;controller receiving API call and calling model functions/returning JSON to frontend here&gt;
  model
    --&lt;model generating PDF &amp; saving it here&gt;
  </code></pre>
  
  <p>The process starts on a form submit in <code>ImageSelect.js</code> where I'm doing a bit of data manipulation and then making the API call to create the PDF before redirecting to <code>ImageDownload</code>:</p>
  
  <pre><code>    const config = {
        method: "POST",
        headers: new Headers({
          Accept: "application/json",
          "Content-Type": "application/json"
        }),
        body: JSON.stringify({
          script: this.state.indexedScript,
          versions: versionSubmit
        })
      };
      fetch(process.env.REACT_APP_URL + "/api/getFinalizedImages", config)
      .then(res =&gt; res.json())
      .then(json =&gt; this.props.handleImageSelect(this.state.indexedScript, versionSubmit, json.pdfLink))
      .then(this.props.history.push("/imageDownload"));
  </code></pre>
  
  <p>The original user input text, selected pictures that make up the PDF, and the PDF filename are all passed to the <code>Root.js</code> router so that they can then be made available to <code>ImageDownload.js</code></p>
  
  <pre><code>handleImageSelect = (indexedScript, versionSubmit, pdfLink) =&gt; {
      this.setState({
        indexedScript: indexedScript,
        versionSubmit: versionSubmit,
        pdfLink: pdfLink
      });
    }
  
      render() {
      return (
          &lt;BrowserRouter&gt;
          &lt;div&gt;
            &lt;... Other Routes Here...&gt;
            &lt;Route path='/imageSelect' render={(props) =&gt; (
              &lt;ImageSelect {...props} script={this.state.submittedScript} 
              handleImageSelect={this.handleImageSelect} /&gt;
            )}/&gt;
            &lt;Route path='/imageDownload' render={(props) =&gt; (
              &lt;ImageDownload {...props} indexedScript={this.state.indexedScript}
              versions={this.state.versionSubmit} pdfLink={this.state.pdfLink} /&gt;
            )}/&gt;          
          &lt;/div&gt;
          &lt;/BrowserRouter&gt;
      );
    }
  </code></pre>
  
  <p>Finally, in <code>ImageDownload.js</code> I'm hoping to receive the dynamically-generated PDF filename from the routing component and pass it to the <code>PDFDisplay.js</code> component for rendering:</p>
  
  <pre><code>import React from 'react';
  import { Document, Page, pdfjs } from 'react-pdf';
  
  pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;
  
  class PDFDisplay extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        numPages: null,
        pageNumber: 1
      }
    }
  
    onDocumentLoadSuccess = ({ numPages }) =&gt; {
      this.setState({ numPages });
    }
  
    render() {
      const { pageNumber, numPages } = this.state;
      const filePath = process.env.PUBLIC_URL + '/client/pdfs/' + this.props.pdf + '.pdf';
      return (
        &lt;div&gt;
          &lt;Document
            file={filePath}
            onLoadSuccess={this.onDocumentLoadSuccess}
          &gt;
          &lt;/Document&gt;
          &lt;p&gt;Page {pageNumber} of {numPages}&lt;/p&gt;
        &lt;/div&gt;
      );
    }
  }
  
  export default PDFDisplay;
  </code></pre>
  
  <p>The issue at present is that <code>ImageDownload</code> view is refreshed several times when the PDF creation form is submitted in <code>ImageSelect</code>; when it is done refreshing none of the props are passed through. I've tried a variety of other approaches like putting the API call into <code>ImageDownload</code>'s <code>componentDidMount</code> (refresh triggered another API call which meant endless refreshes/generated PDFs) and saving the PDF in a backend directory (frontend couldn't access a file outside of its directory for display), so I'm not sure what approach I should be taking here.</p>