Before you start - checklist
Description
Short description of the bug you encountered.
In React PDF v3, the comparison check for updating the PDF was done deeply, if the file prop was an object. This is not the case in React PDF v4, and this results in an infinite update if the object passed as a prop to filename is created in the render function...
Steps to reproduce
I can provide this later, but basically try to render a PDF with an object for the filename, and create the object in the render function.
Expected behavior
I expected it to function the same as React-PDF v3, where it would only update if the underlying value change
Additional information
I'm happy to provide a pull-request, if this is an unintended side-effect, as well as a test case demonstrating this.
Environment
Generally, creating props on the fly during render is not a good idea.
If you pass an object straight into file prop, you're essentially creating a new object with every render. This change in file prop value gets detected by Document. This causes Document to load the file again as it has no way of knowing if it's the same file or not. Only after the file has been loaded again, Document compares file fingerprints, and cancels reloading the document if they are identical.
The best way to avoid it would be to create an object when you get the file data, assign it to state, and pass it as a prop in render function. If you're using hooks, you could also use useMemo hook to achieve the same result.
For simplicity of the examples provided, I'm assuming data comes already in props, but the core idea stays the same if in your case it's one component that is responsible for both getting the data and passing it to React-PDF.
function App({ data }) {
// File object that will only get recreated when `data` changes
const file = useMemo(() => ({ data }), [data]);
return (
<Document file={file}>
// …
</Document>
);
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
file: null,
};
}
componentDidMount() {
const { data } = this.props;
this.setState({ file: { data } });
}
componentDidUpdate(prevProps) {
const { data } = this.props;
if (data !== prevProps.data) {
// Only update state if `data` prop changes
this.setState({ file: { data } });
}
}
render() {
const { file } = this.state;
return (
<Document file={file}>
// …
</Document>
);
}
}
Most helpful comment
Generally, creating props on the fly during render is not a good idea.
If you pass an object straight into
fileprop, you're essentially creating a new object with every render. This change infileprop value gets detected byDocument. This causesDocumentto load the file again as it has no way of knowing if it's the same file or not. Only after the file has been loaded again,Documentcompares file fingerprints, and cancels reloading the document if they are identical.The best way to avoid it would be to create an object when you get the file data, assign it to state, and pass it as a prop in render function. If you're using hooks, you could also use
useMemohook to achieve the same result.For simplicity of the examples provided, I'm assuming
datacomes already in props, but the core idea stays the same if in your case it's one component that is responsible for both getting the data and passing it to React-PDF.React Hooks
Legacy class components