I am using react-file-viewer in React.js app to read docx. I use a template code. I can read the file, but as soon as I dynamically change the filePath - FileViewer does not reload. I can see that my component gets updated with a new filePath, and I can render a new filePath in a container code, so it gets displayed on screen, but the actual FileViewer does not re-render and displays previously loaded document.
5 Answers
Managed to get it working by forcing unmounting of the container that wraps <File Viewer /> as on initial mount the document was loading properly and the only time it was not working was on a re-render. To unmount, I used a key attribute as described here by T.J. Crowder.
// Parent.js
return (
<div>
{this.state.filePath && <FileViewerWrapper filePath={this.state.filePath} />}
<div>
);
// Child.js
render(){
++this.childKey; // initialized in constructor
return (
<FileViewer
fileType='docx'
filePath={path.resolve(__dirname), `path/${this.props.filePath}`}
etc.
/>
);
}
As from the linked answer -
The child will have a new key each time, so React will assume it's part of a list and throw away the old one, creating the new one. Any state change in your component that causes it to re-render will force that unmount-and-recreated behavior on the child.
P.S. Initially I tried to use unmountComponentAtNode method from ReactDOM with a ref on a Child component. Something like this -
let mountNode = ReactDOM.findDOMNode(this.refs.mount);
try {
ReactDOM.unmountComponentAtNode(mountNode);
} catch (e) {
console.error(e);
}
but was getting a warning unmountComponentAtNode(): The node you're attempting to unmount was rendered by React and is not a top-level container. Instead, have the parent component update its state and rerender in order to remove this component. So, the solution with key works for me better.
Comments
make sure to pass unique key if u have multiple documents you want to display.
something like
const FileViewerComponent = (props) => {
const [state, setState] = useState({
type: getFileExtension(props.file.Title),
path: getFilePath(props.file.Path)
});
useEffect(() => {
if (state.path != getFilePath(props.file.Path)) {
setState({
type: getFileExtension(props.file.Title),
path: getFilePath(props.file.Path)
})
}
});
return (<div style={{maxHeight: '700px', 'overflow-y': 'scroll'}}>
<FileViewer
fileType={state.type}
filePath={state.path}
key= {state.path}
/>
</div>
);
};
Comments
In your dynamically change filepath event you can make your file path empty then set the new file path.
constructor() {
this.state = {
filePreviewPath: "",
filePreviewType: "",
};
}
// dynamic load event
this.setState({
filePreviewPath: "",
filePreviewType: ""
});
this.setState({
filePreviewPath: filePathDataUrl, // filePathDataUrl: string = "data:" + mimeTypeforPreview + ";base64, " + fileObj.fileContent;
filePreviewType: filePreviewType // filePreviewType = "pdf"
});
html
<FileViewer
fileType={this.state.filePreviewType}
filePath={this.state.filePreviewPath}
/>