-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uncaught Error: stream.push() after EOF #420
Comments
Had the same error upgrading to 1.0.0 and it was because a second render was accessing the already finished stream from the last render that was still generating the PDF. In my case with React a second render was triggered in componentDidUpdate, while another one triggered by componentDidMount was still running (accessing the same stream) in the background. Solved the issue by creating a sequential queue:
|
I just saw this issue come through from my production app. I'm using the DOM bindings, so have a few levels of abstraction from the |
Hey. Thanks for reporting this. Could someone share a snippet I can use to reproduce this issue? I just tried making updates to a document and everything worked well. This is what I did: class App extends React.Component {
state = { counter: 10 }
componentDidMount() {
setInterval(() => {
if (this.state.counter > 0) {
this.setState({ counter: this.state.counter - 1 })
}
}, 2500)
}
render() {
return (
<PDFViewer style={{ width: '100%', height: '100%' }}>
<Document>
<Page>
<Text>{this.state.counter}</Text>
</Page>
</Document>
</PDFViewer>
)
}
} I basically see the counter being decreased from 10 to 0 in the PDFViewer Chrome version: 71.0.3578.98 (Official Build) (64-bit) |
What happens if you set the interval to something really low like 5. I
think the issue is when the PDF is still rendering but another render is
called. Essentially you need to have two pdf rendering methods overlapping.
Might need to make the PDF more complex to slow down the rendering.
…On Thu, Dec 13, 2018 at 6:29 PM Diego Muracciole ***@***.***> wrote:
Hey. Thanks for reporting this.
Could someone share a snippet I can use to reproduce this issue? I just
tried making updates to a document and everything worked well.
This is what I did:
class App extends React.Component {
state = { counter: 10 }
componentDidMount() {
setInterval(() => {
if (this.state.counter > 0) {
this.setState({ counter: this.state.counter - 1 })
}
}, 2500)
}
render() {
return (
<PDFViewer style={{ width: '100%', height: '100%' }}>
<Document>
<Page>
<Text>{this.state.counter}</Text>
</Page>
</Document>
</PDFViewer>
)
}
}
I basically see the counter being decreased from 10 to 0 in the PDFViewer
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#420 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAcXfDJsUhynDaZJamR4Ne1OmNwd41THks5u4w0hgaJpZM4ZRowr>
.
|
I render |
@bkoltai I tried by also setting the timeout as 5, but still cannot reproduce this. What happens is I get the PDF with a |
I actually think I narrowed my error usecase down to triggering a re-render from a |
Thanks @bkoltai, but isn't my example doing what you said? It's basically triggering a re-render on |
Close I think, but might be different if it was just an immediate setState
rather than in a setInterval
…On Tue, Jan 15, 2019 at 6:58 PM Diego Muracciole ***@***.***> wrote:
Thanks @bkoltai <https://github.com/bkoltai>, but isn't my example doing
what you said? It's basically triggering a re-render on componentDidMount
when the counter get's decreased
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#420 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAcXfNQhzcEFInhmqX81XsyYzNnh0L80ks5vDpVNgaJpZM4ZRowr>
.
|
Oh, got it! Going to try that. Thanks! |
Thanks to @bkoltai comment I manage to do this by making sure only 1 of the PDF will load at a time. This way there will never be any re-render and only 1 PDF component will render at a time. A bit troublesome but it works |
I am also getting this error but in addition to the above problems my initial pdf document does not contain the properly rendered information. In the below example, this code should render a simple page with text on it but instead renders two blank pages:
In a more complicated version of the above code, I have the ability to change the props of the component that renders the pdf. When the props change, the pdf document renders properly. Noting issue #462 - the error message for that issue on codesandbox is "Potential infinite loop". Given that the above suggestions are about multiple renders causing the "stream.push() after EOF" error - could there be a correlation between these two issues? I LOVE this library - thank you so much for creating it! |
We are also seeing this in Firefox and IE too. @diegomura I wonder if we should look in to @naminho’s insight that “a second render was triggered in componentDidUpdate, while another one triggered by componentDidMount was still running (accessing the same stream) in the background”. I’m not sure where to start following the logic to find which stream is being pushed to after EOF, though. I think this may (or may not) be related to #476, as we are also not seeing the |
Same problem here, including Chrome :( |
I faced the same problem while rendering |
I couldn't get this to work in a modal, but I have a temporary hack that makes the page load, even when it's receiving props from another page. In this example I'm sending state from a Link to my Publish page, which generates a PDF. On the first page we... Then that loads the below page...
This successfully loads the value from the previous page by pausing the rendering of the Publish page for 1ms while the props & state sort themselves out. Then, with that 1ms passed, the page renders once and react-pdf successfully renders. THIS IS HACKY! But I suppose it's a temp workaround until this multiple-render issue in react-pdf is resolved. Hope this helps someone (or feel free to tell me that I've broken the universe with this fix and that I shouldn't do it) |
I tried it now, it seems work correctly 👍 |
I solved my version of this issue with shouldComponentUpdate and the onRender callback.
|
Could you please release the whole snippet? We're facing the same issue. |
That was basically it. The If you are re-rendering and changing the document this workaround could be better:
There are probably better ways. But as I understand it you can't update a document that is still rendering and since the pdf-rendering is async there is no guarantee that it's finished the next time the component want's to render. So either you'll take full control of when it updates ( 1st example) which says only render one at a time, or you just un-mount/re-mount the component with updated |
@sondretj Thank you so much. We've now adapted your solution to React hooks and Functional components for our Typescript based app, and we'd like to share it here:
|
I used this method for an on-the-fly pdf download where:
Here is the method that is calling it (lives inside a React Component)
The timeouts in the first method help only in the sense that they remove the render itself from the state cycle, and to let the document render the new div we are appending, respectively. So the setStates Im using for the loading behavior (showing a spinner next to the pdf icon while the PDF content renders) can be set while the pdf render does its thing. Note: this.getPDFContent() just return the Document I want to render using the ReactPDF components Pros:
Cons:
In general Ive found that if I try to render the PDF while setting state for the component containing it that I get the stream.push() error. This makes it hard to render and hard to update the content, so I just create, render, and download the content on the fly using the above methodology until the core issue is fixed. |
@youngbw I like your hack if you use the render props it gets easier:
|
Wow, this thread has been really helpful. I was having similar errors attempting to render PDFDownloadLinks inside of react-inclusive-sortable-table. Every time I went to sort the table, I'd get a WSOD, because it was trying to re-render all the PDFs. Anyways, I thought it might be helpful for posterity to document a component that puts together what @egemenuzunali and @youngbw were describing above: import React from 'react';
import { render } from 'react-dom';
import PropTypes from 'prop-types';
import Download from '../table/assets/icon-download.svg';
import { PDFDownloadLink } from '@react-pdf/renderer';
import slugify from 'slugify';
import CertificatePDF from './certificate-pdf';
export default class PDFLink extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false
};
this.generatePDF = this.generatePDF.bind(this);
this.buildPDF = this.buildPDF.bind(this);
this.createAndDownloadPDF = this.createAndDownloadPDF.bind(this);
}
createAndDownloadPDF(pdfContent, filename, divId, callback) {
setTimeout(() => {
const link = (
<div id={divId}>
<PDFDownloadLink document={pdfContent} fileName={filename}>
{({ loading }) => {
if (!loading) {
setTimeout(() => {
document.getElementById(divId).children[0].click();
callback();
}, 3);
}
}}
</PDFDownloadLink>
</div>
);
const elem = document.createElement('div');
document.getElementById('___gatsby').appendChild(elem);
render(link, elem);
}, 50);
}
buildPDF() {
if (!this.state.loading) {
this.setState({ loading: true }, () => {
this.createAndDownloadPDF(
this.generatePDF(),
`${slugify(this.props.title).toLowerCase()}.pdf`,
`${slugify(this.props.title).toLowerCase()}`,
() => this.setState({ loading: false })
);
});
}
}
generatePDF() {
// CertificatePDF is a component that returns a PDF <Document />
return <CertificatePDF {...this.props} />;
}
render() {
return this.state.loading ? (
'Loading...'
) : (
<button onClick={this.buildPDF}>
<Download
title={`Click here to download a certificate for ${this.props.title}`}
height="15"
width="12"
/>
</button>
);
}
}
PDFLink.propTypes = {
/* User name on the certificate */
name: PropTypes.string.isRequired,
/* Title of the course */
title: PropTypes.string.isRequired,
/* Date of completion */
date: PropTypes.string.isRequired,
/* Number of credits earned */
credits: PropTypes.string.isRequired
}; |
This hack worked for me very good! Thanks a lot. It works for both PDFViewer and PDFDownladLink in the same Modal. That you sooooo much!! |
This is my code for this and it works. Thanks to @modemmute import React, { Component } from 'react';
import ReactPDF, {
Document, Page, Text, View, StyleSheet, PDFViewer, PDFDownloadLink,
} from '@react-pdf/renderer';
import {
Modal, ModalHeader, ModalBody, ModalFooter,
} from 'reactstrap';
class MyComponent extends Component {
constructor(props) {
super(props);
this.state = {
showing: false,
ready: false,
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState((prevState) => ({
showing: !prevState.showing,
ready: false,
}), () => { // THIS IS THE HACK
setTimeout(() => {
this.setState({ ready: true });
}, 1);
});
}
render() {
const { showing, ready } = this.state;
const styles = StyleSheet.create({
page: {
flexDirection: 'row',
backgroundColor: '#FFF',
},
section: {
margin: 10,
padding: 10,
flexGrow: 1,
fontSize: 100,
fontWeight: 700,
color: '#CCC',
},
text: {
color: '#F00',
margin: 10,
padding: 10,
flexGrow: 1,
fontSize: 12,
},
});
const doc = (
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>This is a title</Text>
</View>
</Page>
</Document>
);
return (
<>
<Modal isOpen={showing} toggle={this.toggle}>
<ModalHeader toggle={this.toggle} />
<ModalBody>
{ready && (
<PDFViewer>
{doc}
</PDFViewer>
)}
</ModalBody>
<ModalFooter>
{ready && (
<PDFDownloadLink document={doc} fileName="test.pdf">
{
({ blob, url, loading, error }) => (loading ? 'Loading document...' : 'Download')
}
</PDFDownloadLink>
)}
</ModalFooter>
</Modal>
</>
);
}
}
export default MyComponent; |
After quite some time struggling, I've used a PureComponent and it works perfectly. |
Same problem when using functional component to return PDFDownloadLink. Component works fine when the Document only has a page component as a child, but is the page has children it fails to render |
In our case, the document component had required prop "data", which could be changed async.
|
I am still getting the error. Can someone help me what I am missing? |
@phoenixTW I've never worked with the memo, but this code should work
if not ready don't return PDFDownloadLink at all, just return null or a placeholder button. |
Hey guys, there's a lot of code snippets above, but I've managed to fix the issue simply by putting dummy button that says 'Generate PDF' which triggers ready state, and with if check loaded PDFDownloadLink. Otherwise it would just rerender every time a component loads, which brought up the error. Hope this helps someone.
|
I figured out, it was the data change which was causing this problem, so I used |
Here's how I fixed the problem, I hope it will help others.
const GeneratePDF = props => {
const [isShowBlobProvider, setIsShowBlobProvider] = useState(false);
const downloadFullPDF = (blob, filename) => {
const a = document.createElement('a');
document.body.appendChild(a);
a.style = 'display: none';
a.href = window.URL.createObjectURL(blob);
a.download = filename;
a.click();
window.URL.revokeObjectURL(a.href);
a.remove();
setIsShowBlobProvider(false);
};
return (
<div>
{
isShowBlobProvider? (
<BlobProvider document={<Document />} fileName="somename.pdf">
{({ blob, url, loading, error }) => {
if (!loading) downloadFullPDF(blob, 'somename.pdf');
return (
<Button disabled variant="text" color="primary">
<CircularProgress size={24} />
Exporting
</Button>
);
}
)
}
<Button onClick={() => setIsShowBlobProvider(true)} variant="text" color="primary">
Export
</Button>
</div>
);
}; |
hi i am new to react but i got this problem as well. could you explain how to set a re-render on componentDidMount? |
Please Help i got this error
|
Very good, Thanks @sebastijandumancic I hadn't thought about it, it worked well |
I was facing this problem. But finally I got the solution. So first of all make a state as following and then use it as conditionally at component return. const [isReady, setIsReady] = useState(false); useEffect(()=> { return ( |
What the hell are all these weird workarounds etc. const DownloadPdf = () => {
return useMemo(
() => (
<PDFDownloadLink document={<MyDocument />} fileName="some-nane.pdf">
{({ loading }) => (loading ? 'loading...' : 'download')}
</PDFDownloadLink>
),
[],
)
} |
I believe folks like myself need to render it multiple times when the state it renders is changed. I too had to do a similar work-around. |
In that case something like this would probably work: const RerenderablePDF = (props) => {
return useMemo(
() => (
<PDFViewer key={Math.random()}>
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>This is a title</Text>
</View>
</Page>
</Document>
</PDFViewer>
),
[props],
)
}
Changing the key and not reusing the previous rendered component is key. 😉 |
Finally found a use-case for useMemo(). @markpradhan Thank you! Works really well |
@markpradhan what if I'm not using functional component and have a Class component? |
Haven't tested it, but this should work. class RerenderablePDF extends React.PureComponent {
render() {
const { whateverIsInProps } = this.props
return (
<PDFViewer key={Math.random()}>
<Document>
<Page size="A4" style={styles.page}>
<View style={styles.section}>
<Text>This is a title</Text>
</View>
</Page>
</Document>
</PDFViewer>
)
}
} Just make sure whatever you pass to props doesn't change unless you want the pdf to rerender. If updates happen frequently debouce/throttle them. |
The above error was due to PDFViewer rendering in DOM as it's suppose to do based on the library but since the first render happens when the modal opens, the browser keeps that. when you close the modal, if there is no way to tell the browser something happen, it assumes there is we still at the previous render and so react-pdf Component(PDFViewer) will be still in the DOM without and instructions to render something, that's why on the second opening of the modal, it break with these error and no document to show. import React, { useEffect, useState } from "react";
import Modal from "reactstrap/lib/Modal";
import { Button, ModalBody, ModalHeader } from "reactstrap";
import PropTypes from "prop-types";
import { Document, Page } from "react-pdf";
import { BlobProvider } from "@react-pdf/renderer";
import DocumentViewer from "./Documents/DocumentViewer";
const orngPrint = "/img/brand/print.png";
const MyDoc = props => {
// need to make props available for the document in BlobProvider
return <DocumentViewer {...props} />;
};
const ModalViewer = props => {
const [open, setOpen] = useState(false);
const [numPages, setNumPages] = useState(1);
const [pageNumber, setPageNumber] = useState(1);
const [url, setUrl] = useState(null);
const { stateChange, onExit, modalTitle } = props;
useEffect(() => {
// We are making a update to state so that when the modal closes, the document
// has a way to know that something happended in the component
// this is useful in this context because react-pdf/renderer has to be aware of the
// update to render our document correctly.
// Refer to this issues for more insights:
// - https://github.com/diegomura/react-pdf/issues/420
// - https://github.com/diegomura/react-pdf/issues/971
setOpen(false);
setOpen(true);
return () => setOpen(false);
}, []);
const handleExit = () => {
onExit();
setOpen(false);
};
const onDocumentLoadSuccess = ({ numPages }) => {
setNumPages(numPages);
};
return (
<div>
<Modal isOpen={true}>
<ModalHeader className="document-modal">
<Button className="cross-btn">
<i onClick={handleExit} className="fas fa-times" />
</Button>
</ModalHeader>
<ModalBody>
<div className="document-viewer-container">
{open && (
<BlobProvider document={MyDoc(props)}>
{({ url }) => {
// setUrl(url); # NOTE: you don't want to do this here. It creates another error
// The exatra Document is for rendering in the DOM
// since react/renderer does support dom rendering but
// it isn't stable. Requires to much configuration
return (
<Document
className={"document-inner"}
file={url}
onLoadSuccess={onDocumentLoadSuccess}
>
<Page
className={"document-container"}
pageNumber={pageNumber}
/>
</Document>
);
}}
</BlobProvider>
)}
</div>
</ModalBody>
</Modal>
</div>
);
};
ModalViewer.propTypes = {
modalTitle: PropTypes.string.isRequired
};
export default ModalViewer;
|
I fixed the issue when using a functional component, pdf viewer inside the modal. `const DocumentPDFGenerator = ({ modalHandler }: IPdfGeneratorProps) => {
} export default DocumentPDFGenerator` |
Follow my code without solving the problem. import React, { useState, useEffect, Fragment } from "react";
import { PDFViewer, Document, Page } from "@react-pdf/renderer";
import {
Table,
TableHeader,
TableCell,
TableBody,
DataTableCell,
} from "@david.kucsai/react-pdf-table";
import api from "./services/api";
import "./App.css";
function App() {
const [loading, setLoading] = useState(true);
const [condicoes, setCondicoes] = useState([]);
useEffect(() => {
async function loadCondicoes() {
const result = await api.get("condicoes");
setCondicoes(result.data);
if (result.data) {
setLoading(false);
//console.log(secoes);
}
}
loadCondicoes();
}, [loading]);
return (
<Fragment>
{loading && condicoes.length > 0}
<PDFViewer width="1000" height="600" className="app">
<Document>
<Page size="A4">
<Table data={condicoes}>
<TableHeader>
<TableCell>Id</TableCell>
<TableCell>Descrição</TableCell>
<TableCell>Pontuação</TableCell>
</TableHeader>
<TableBody>
<DataTableCell getContent={(r) => r.id} />
<DataTableCell getContent={(r) => r.descricao} />
<DataTableCell getContent={(r) => r.pontuacao} />
</TableBody>
</Table>
</Page>
</Document>
</PDFViewer>
</Fragment>
);
}
export default App; |
for me what i need, is to generate an invoice and upload it to the cloud as a PDF when the user clicks the generate button, |
OS: Chrome Version 70.0.3538.110 (official Build) (64 bit)
React-pdf version: 1.0.0
"@react-pdf/renderer": "^1.0.0",
"@react-pdf/styled-components": "^1.2.0",
Description: With the last update made 7 days ago the console show this error, before the update the pdf works correctly.
_stream_readable.js:271 Uncaught Error: stream.push() after EOF
at readableAddChunk (_stream_readable.js:271)
at PDFDocument../node_modules/readable-stream/lib/_stream_readable.js.Readable.push (_stream_readable.js:245)
at PDFDocument._write (pdfkit.browser.es.js:3731)
at PDFReference.finalize (pdfkit.browser.es.js:255)
at PDFReference.end (pdfkit.browser.es.js:247)
at PNGImage.finalize (pdfkit.browser.es.js:3162)
at pdfkit.browser.es.js:3202
at Deflate.onEnd (index.js:225)
at Deflate../node_modules/events/events.js.EventEmitter.emit (events.js:96)
at endReadableNT (_stream_readable.js:1010)
at afterTickTwo (index.js:27)
at Item../node_modules/process/browser.js.Item.run (browser.js:153)
at drainQueue (browser.js:123)
The text was updated successfully, but these errors were encountered: