Search code examples
reactjstypescriptreact-pdf

How to use react-pdf library with Typescript


I'm trying to use a npm library that help me to render a pdf document. I found react-pdf but it doesn't have the types for TypeScript yet, so I'm using it as I show below:

let Document = require('react-pdf');
let Page = require('react-pdf');

class PdfViewer extends React.Component<PdfViewer.Props, PdfViewer.State> {

  constructor(props: PdfViewer.Props) {
    super(props);
    this.state = {
      numPages: null,
      pageNumber: 1,
    };

  }
  onDocumentLoadSuccess = ( numPages: number ) => {
    this.setState({ numPages });

  }

  componentWillReceiveProps(nextProps: PdfViewer.Props) {
    // this.setState(CsvViewer.parse(nextProps.data));
  }

  render() {
      const {url} = this.props;
      const { pageNumber, numPages } = this.state;

      const buffer = data as ArrayBuffer;

      return ( 
      <div>
        <Document
          file={url}
          onLoadSuccess={this.onDocumentLoadSuccess}
        >
          <Page pageNumber={pageNumber} />
        </Document>
        <p>Page {pageNumber} of {numPages}</p>
      </div>
      );

  }
}

But it's throwing this error:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.

I found this in GitHub but it didn't help me to much. I went to the documentation of the library, and I passed an object in the file parameter as they show to do but the same error. I think is something else. I went to the source code of the react-pdf library and I think this is the function they use to get the file from the parameter:

findDocumentSource = async () => {
    const { file } = this.props;

    if (!file) {
      return null;
    }

    // File is a string
    if (typeof file === 'string') {
      if (isDataURI(file)) {
        const fileUint8Array = dataURItoUint8Array(file);
        return { data: fileUint8Array };
      }

      displayCORSWarning();
      return { url: file };
    }

    // File is PDFDataRangeTransport
    if (file instanceof PDFDataRangeTransport) {
      return { range: file };
    }

    // File is an ArrayBuffer
    if (isArrayBuffer(file)) {
      return { data: file };
    }

    /**
     * The cases below are browser-only.
     * If you're running on a non-browser environment, these cases will be of no use.
     */
    if (isBrowser) {
      // File is a Blob
      if (isBlob(file) || isFile(file)) {
        return { data: await loadFromFile(file) };
      }
    }

    // At this point, file must be an object
    if (typeof file !== 'object') {
      throw new Error('Invalid parameter in file, need either Uint8Array, string or a parameter object');
    }

    if (!file.url && !file.data && !file.range) {
      throw new Error('Invalid parameter object: need either .data, .range or .url');
    }

    // File .url is a string
    if (typeof file.url === 'string') {
      if (isDataURI(file.url)) {
        const { url, ...otherParams } = file;
        const fileUint8Array = dataURItoUint8Array(url);
        return { data: fileUint8Array, ...otherParams };
      }

      displayCORSWarning();
    }

    return file;
  };

But I doubt the error is here but I can't figure out what could be. The version of react I'm using is 16.8.2 and 4.0.2 for react-pdf


Solution

  • This is incorrect:

    let Document = require('react-pdf');
    let Page = require('react-pdf');
    

    You are importing the entire module when you do this. That means that Document and Page both contain all the modules of react-pdf. Instead what you want to do is either import ReactPDF and reference those modules:

    const ReactPDF = require('react-pdf');
    
    <ReactPDF.Document />
    <ReactPDF.Page />
    

    or you could use destructuring:

    import { Document, Page } from 'react-pdf';
    
    //or
    
    const { Document, Page } = require('react-pdf'); 
    

    If TypeScript complains about missing declarations, just add a @types folder to your project, create a subfolder inside of it called react-pdf and file inside that folder called index.d.ts with one line:

    declare module "react-pdf"