I want to pass a dynamic pdf file from BooksList component to Link component in react-router-dom so that when I click the BooksList component inside a Link a pdf file will be passed to Link, then Link will forward this pdf file to PDFViewer component where this pdf file will be rendered. If it is impossible can you please suggest ways I can use to achieve this functionality. The example of the code before any data is passed is as follows:
<Link to="/pdf_viewer">
<BooksList></BooksList>
</Link>
<Route path="/pdf_viewer">
<PDFViewer></PDFViewer>
</Route>
BooksList
export class BooksList extends Component {
render() {
return this.props.books.map((book)=> (
//Here there is book info rendered from props
//PDF file is an attribute in book eg. book.pdf_file
<div>Title: {book.book_title}</div>
));
}
}
export default BooksList
PDFViewer
export class PDFViewer extends Component {
constructor(props){
super(props);
const { state: { pdfFile } } = this.props.location;
}
render() {
return (
<div key={book._id} className="pdfViewer">
<embed src={ `data:application/pdf;base64, ${this.state}` } width="100%" height="600px"></embed>
</div>
)
}
}
export default PDFViewer
UnLinkedBooksList
export class UnLinkedBooksList extends Component {
viewPDFHandler = pdfFile => {
const {history} = this.props;
history.push({
pathname: "/pdfviewer",
state: {
pdfFile
}
});
}
render() {
return this.props.books.map((book)=> (
<div>
<button type="button" onClick={()=> this.viewPDFHandler(book.pdf_file_byte["$binary"])}>View PDF</button>
</div>
));
}
}
const BooksList = withRouter(UnLinkedBooksList);
export default UnLinkedBooksList
App
export class App extends Component {
state = {
books: []
}
componentDidMount(){
axios.get("http://127.0.0.1:5000/get_all_books").then(res=>this.setState({books:res.data.all_books}))
}
render() {
return (
<Router>
{/* Index Page */}
<Route exact path="/">
<div className="App">
<SearchBar></SearchBar>
<div className="row p-10">
<div className="col-lg-6">
<Link to="/pdf_viewer" className="text-decoration-none">
<BooksList books={this.state.books}></BooksList>
</Link>
</div>
</div>
</div>
</Route>
{/* PDF Viewer Page */}
<Route path="/pdf_viewer" className="text-decoration-none">
<PDFViewer></PDFViewer>
</Route>
</Router>
)
}
}
export default App
Expose the history
object from the routing context in BooksList
and add a handler for each mapped book to pass the PDF file reference as a route transition payload. Since BooksList
is a class-based component and isn't rendered by a Roiute
(it probably should be, btw), use the withRouter Higher Order Component to decorate and inject route props, (history
, location
, match
).
class UnlinkedBooksList extends Component {
viewPdfHandler = pdfFile => {
const { history } = this.props;
history.push({
pathname: "/pdf_viewer",
state: {
pdfFile
}
});
};
render() {
return this.props.books.map((book)=> (
//Here there is book info rendered from props
//PDF file is an attribute in book eg. book.pdf_file
...
<button
type="button"
onClick={() => viewPdfHandler(book.pdf_file)}
>
View PDF
</button>
));
}
}
const BooksList = withRouter(UnlinkedBooksList);
export default BooksList;
In App, default import BooksList
, this is the linked component that will have the location
prop.
import BooksList from './path/to/BooksList';
On the receiving end you can extract the route state. Render PDFView
on the component
prop so it also receives the route props, specifically, location
so it can access the route state.
<Route path="/pdf_viewer" component={PDFViewer} />
In PDFViewer, access pdfFile
from the location
object:
const { state: { pdfFile } } = this.props.location;