Search code examples
javascriptreactjsdomreact-pdf

react-pdf - Error: Target container is not a DOM element


I am using React with react-pdf and trying to render a PDF into my modal. However, my modal gets loaded on a button click, so it's not loaded on app load. But it is trying to call the PDF rendering call on app load and thus generating the error: 'Error: Target container is not a DOM element'. I'm not sure of how to fix this issue.

Here is my entire modal component:

import ReactModal from 'react-modal';
import React, { useEffect, useRef } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import ReactPDF, { PDFViewer } from '@react-pdf/renderer';
import CloseButton from './CloseButton';
import MessageHub from '../message/MessageHub';
import PDFPlacard from '../placards/PDFPlacard';

const PDF = () => (
    <PDFViewer>
        <PDFPlacard />
    </PDFViewer>
);

const PlacardsModal = (props) => {
    const { openPlacards, setOpenPlacards, customStyles } = props;
    const numPlacards = React.createRef();

    const ref = useRef(null);

    // this call needs to happen after the modal and #PDFPlacard is rendered
    ReactDOM.render(<PDF />, document.getElementById('PDFPlacard'));

    const handleSubmit = (evt) => {
        evt.preventDefault();
        if (numPlacards.current.value === '') {
            ref.current('Please fill in the number of placards.');
        } else {
            // submit form
        }
    };

    return (
        <ReactModal
            isOpen={openPlacards}
            style={customStyles}
            className={'print-placards-modal'}
            closeTimeoutMS={1000}
        >
            <CloseButton setOpenModal={setOpenPlacards} />
            <h2>Print Placards</h2>
            {/* eslint-disable-next-line react/no-children-prop */}
            <MessageHub children={(add) => (ref.current = add)} />
            <form className={'form'} onSubmit={handleSubmit}>
                <div className={'form-group row'}>
                    <label
                        htmlFor={'numPlacards'}
                        className={'col-sm-6 col-form-label'}
                    >
                        Number of Placards:
                    </label>
                    <div className={'col-sm-6'}>
                        <input
                            id={'numPlacards'}
                            type={'number'}
                            className={'form-control'}
                            ref={numPlacards}
                        />
                    </div>
                </div>
                <button
                    className={'btn btn-primary d-block mx-auto mb-2'}
                    type={'submit'}
                    value={'Print'}
                />
            </form>
            <div id={'PDFPlacard'} />
        </ReactModal>
    );
};

PlacardsModal.propTypes = {
    openPlacards: PropTypes.bool,
    setOpenPlacards: PropTypes.func,
    customStyles: PropTypes.object,
    title: PropTypes.string
};

export default PlacardsModal;

Here is PDFPlacard.js:

import React from 'react';
import {
    Page,
    Text,
    View,
    Document,
    StyleSheet
} from '@react-pdf/renderer';

// Create styles
const styles = StyleSheet.create({
    page: {
        flexDirection: 'row',
        backgroundColor: '#E4E4E4'
    },
    section: {
        margin: 10,
        padding: 10,
        flexGrow: 1
    }
});

// Create Document Component
const PDFPlacard = (type) => (
    <Document>
        <Page size="letter" style={styles.page}>
            <View style={styles.section}>
                <Text>Section #1</Text>
            </View>
            <View style={styles.section}>
                <Text>Section #2</Text>
            </View>
        </Page>
    </Document>
);

export default PDFPlacard;

And my index.js:

import React from 'react';
import { render } from 'react-dom';
import 'bootstrap/dist/css/bootstrap.min.css';
import 'bootstrap/dist/js/bootstrap.min';
import './index.css';
import App from './App';

const title = 'title';

render(<App title={title} />, document.getElementById('root'));

// eslint-disable-next-line no-undef
module.hot.accept();

The parent is loading the PlacardsModal like this:

...
<button
  className={'btn btn-info mb-3'}
  onClick={() => setOpenPlacards(true)}
>
  Placards
</button>
...
<PlacardsModal
  openPlacards={openPlacards}
  setOpenPlacards={setOpenPlacards}
  customStyles={customStyles}
/>

Solution

  • Finally got the answer from another source. I should not have been making that ReactDOM.render( call at all. I should just have been including the PDF components on my page. Like this:

    ...
                    <button
                        className={'btn btn-primary d-block mx-auto mb-2'}
                        type={'submit'}
                        value={'Print'}
                    />
                </form>
                <!-- not this -->
                <div id={'PDFPlacard'} />
                <!-- this -->
                <PDF />
            </ReactModal>
        );
    };
    ...