使用pdfjs-dist+React开发PDF阅读器时遇TypeError错误求助
使用React、pdfjs-dist和Bootstrap实现PDF阅读器时遇到TypeError问题
我正在尝试用pdfjs-dist、React和Bootstrap开发一个PDF阅读器,功能逻辑简单,但每次上传PDF文件运行应用时,都会出现错误:caught TypeError: Cannot read properties of undefined (reading 'current'),无法定位问题所在。我的worker-url版本为1.1.0,pdfjs-dist版本为2.16.105,是跟着YouTube教程开发的,但教程中未出现该问题,附上我的代码:
import React, { useState } from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; import 'bootstrap/dist/js/bootstrap.bundle.min.js'; import 'pdfjs-dist/web/pdf_viewer.css'; import 'pdfjs-dist/build/pdf.worker.min'; import './App.css'; import {Viewer, Worker} from '@react-pdf-viewer/core' import { defaultLayoutPlugin } from '@react-pdf-viewer/default-layout' import '@react-pdf-viewer/core/lib/styles/index.css' import '@react-pdf-viewer/default-layout/lib/styles/index.css' function App() { const defaultLayoutPluginInstance = defaultLayoutPlugin( ); const [pdfFile, setPDFFile] = useState(null) const [viewPDF, setViewPDF] = useState(null) const fileType = ['application/pdf'] const handleChange = (e) => { let selectedFile = e.target.files[0] if(selectedFile) { if(selectedFile && fileType.includes(selectedFile.type)) { let reader = new FileReader() reader.readAsDataURL(selectedFile) reader.onload = (e) => { setPDFFile(e.target.result) } } else { setPDFFile(null) } } else { console.log("please select") } } const handleSubmit = (e) => { e.preventDefault() if(pdfFile !== null) { setViewPDF(pdfFile) } else { setViewPDF(null) } } return ( <div className="container-fluid h-100"> <div className="row h-100"> <div className="col-3 px-1 bg-light position-fixed" id="sticky-sidebar"> <div className="accordion accordion-flush" id="accordionFlushExample"> <div className="accordion" id="accordionExample"> <div className="accordion-item"> <h2 className="accordion-header"> <button className="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne"> Accordion Item #1 </button> </h2> <div id="collapseOne" className="accordion-collapse collapse show" data-bs-parent="#accordionExample"> <div className="accordion-body"> <strong>This is the first item's accordion body.</strong> It is shown by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow. </div> </div> </div> <div className="accordion-item"> <h2 className="accordion-header"> <button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"> Accordion Item #2 </button> </h2> <div id="collapseTwo" className="accordion-collapse collapse" data-bs-parent="#accordionExample"> <div className="accordion-body"> <strong>This is the second item's accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow. </div> </div> </div> <div className="accordion-item"> <h2 className="accordion-header"> <button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree"> Accordion Item #3 </button> </h2> <div id="collapseThree" className="accordion-collapse collapse" data-bs-parent="#accordionExample"> <div className="accordion-body"> <strong>This is the second item's accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow. </div> </div> </div> <div className="accordion-item"> <h2 className="accordion-header"> <button className="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour"> Accordion Item #4 </button> </h2> <div id="collapseFour" className="accordion-collapse collapse" data-bs-parent="#accordionExample"> <div className="accordion-body"> <strong>This is the third item's accordion body.</strong> It is hidden by default, until the collapse plugin adds the appropriate classes that we use to style each element. These classes control the overall appearance, as well as the showing and hiding via CSS transitions. You can modify any of this with custom CSS or overriding our default variables. It's also worth noting that just about any HTML can go within the <code>.accordion-body</code>, though the transition does limit overflow. </div> </div> </div> </div> </div> </div> <div className={`col-9 offset-3 h-100 main-area`} id="main"> <form onSubmit={handleSubmit}> <input type="file" className='form-control' onChange={handleChange}/> <button type='submit' className='btn btn-success'>View PDF</button> </form> <h2>View PDF</h2> <div className='pdf-container'> <Worker workerUrl="https://unpkg.com/pdfjs-dist@2.16.105/build/pdf.worker.min.js"> {viewPDF && <> <Viewer fileUrl={viewPDF} plugins={[defaultLayoutPluginInstance]}/> </>} {!viewPDF && <> No PDF</>} </Worker> </div> </div> </div> </div> ); } export default App;
问题排查与修复方案
这个错误通常是Viewer组件渲染时依赖的DOM元素未初始化,或插件实例创建时机不当导致,以下是具体修复步骤:
- 给PDF容器添加固定尺寸
@react-pdf-viewer/core的Viewer组件需要父容器有明确宽高,否则会导致DOM引用获取失败。在App.css中添加:
.pdf-container { height: 80vh; width: 100%; margin-top: 1rem; }
- 用useMemo确保插件实例只初始化一次
将插件实例创建逻辑用useMemo包裹,避免组件重渲染时重复创建:
import { useMemo } from 'react'; // App组件内部 const defaultLayoutPluginInstance = useMemo(() => defaultLayoutPlugin(), []);
- 简化文件读取逻辑
Viewer支持直接接收File对象作为fileUrl,无需转成DataURL,修改handleChange和handleSubmit:
const handleChange = (e) => { const selectedFile = e.target.files[0]; if (selectedFile && fileType.includes(selectedFile.type)) { setPDFFile(selectedFile); } else { setPDFFile(null); } }; const handleSubmit = (e) => { e.preventDefault(); setViewPDF(pdfFile); };
- 移除冲突的Worker引入
你已在Worker组件中指定了CDN地址,移除本地的import 'pdfjs-dist/build/pdf.worker.min';,避免资源冲突。
调整后的核心代码片段:
import React, { useState, useMemo } from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; import '@react-pdf-viewer/core/lib/styles/index.css'; import '@react-pdf-viewer/default-layout/lib/styles/index.css'; import './App.css'; import { Viewer, Worker } from '@react-pdf-viewer/core'; import { defaultLayoutPlugin } from '@react-pdf-viewer/default-layout'; function App() { const defaultLayoutPluginInstance = useMemo(() => defaultLayoutPlugin(), []); const [pdfFile, setPDFFile] = useState(null); const [viewPDF, setViewPDF] = useState(null); const fileType = ['application/pdf']; const handleChange = (e) => { const selectedFile = e.target.files[0]; if (selectedFile && fileType.includes(selectedFile.type)) { setPDFFile(selectedFile); } else { setPDFFile(null); } }; const handleSubmit = (e) => { e.preventDefault(); setViewPDF(pdfFile); }; return ( <div className="container-fluid h-100"> <div className="row h-100"> <div className="col-3 px-1 bg-light position-fixed" id="sticky-sidebar"> {/* 侧边栏折叠面板内容保持不变 */} </div> <div className={`col-9 offset-3 h-100 main-area`} id="main"> <form onSubmit={handleSubmit}> <input type="file" className='form-control' onChange={handleChange} /> <button type='submit' className='btn btn-success mt-2'>View PDF</button> </form> <h2 className="mt-3">View PDF</h2> <div className='pdf-container'> <Worker workerUrl="https://unpkg.com/pdfjs-dist@2.16.105/build/pdf.worker.min.js"> {viewPDF ? ( <Viewer fileUrl={viewPDF} plugins={[defaultLayoutPluginInstance]} /> ) : ( <p>No PDF selected</p> )} </Worker> </div> </div> </div> </div> ); } export default App;
内容的提问来源于stack exchange,提问作者pau_2513
相关产品推荐
相关产品推荐

