React.memo组件中PDF URL变更时未重新渲染的问题排查
React.memo 配合 react-pdf 组件不更新的问题修复
问题描述
使用 react-pdf 渲染PDF文件时,尝试通过 React.memo 控制仅当PDF URL变化时重新渲染组件,但点击按钮切换URL后,组件从未更新,无法加载新的PDF文件。
问题原因分析
- React.memo 比较函数逻辑颠倒:
React.memo的第二个参数返回true表示不需要重新渲染,返回false才需要重新渲染。当前代码中返回的是prevProps.curPdfUrl !== nextProps.curPdfUrl,即URL变化时告诉React不要重新渲染,完全搞反了逻辑。 - 固定渲染单页PDF:
renderPages始终传入固定值1,且未通过Document的onLoadSuccess获取实际PDF总页数,导致页面数量不会随新PDF变化。 - 初始状态空值(次要):Home组件中
pdfUrl初始为空字符串,第一次点击按钮前App组件无有效内容可渲染。
修复步骤
1. 修正 React.memo 比较函数逻辑
将比较函数返回值取反,当URL相同时返回 true(不重新渲染),URL不同时返回 false(触发重新渲染):
}, (prevProps, nextProps) => { // URL相同则不重新渲染,不同则重新渲染 return prevProps.curPdfUrl === nextProps.curPdfUrl; })
2. 动态获取并渲染PDF总页数
在App组件中添加状态保存总页数,通过 Document 的 onLoadSuccess 回调更新页数:
// 新增状态保存总页数 const [totalPages, setTotalPages] = useState<number>(0); // 更新onLoadSuccess回调 const docuemtLocadSuccess = (pdf: DocumentCallback) => { setTotalPages(pdf.numPages); } // 修改Document子组件调用,传入实际总页数 <Document options={options} file={curPdfUrl} onLoadSuccess={docuemtLocadSuccess}> {renderPages(totalPages)} </Document>
3. 修复options中的httpHeaders错误(可选)
当前代码中 "getAccessToken()" 是字符串而非函数调用,需替换为实际的token获取逻辑:
httpHeaders: { 'Authorization': 'Bearer ' + getAccessToken() // 替换为你的token获取函数 }
4. 优化初始状态(可选)
给Home组件的 pdfUrl 设置初始有效URL,避免首次点击前无内容渲染:
const [pdfUrl, setPdfUrl] = useState<string>("https://pdf.top/tex/static/proj/2023/9/287ea73d1c78463298e9e5b267cab87f/main.pdf?v=ba9e5b5fda754e72a20");
修改后的完整App.tsx代码
import React, { useRef, useState } from 'react'; import { Page, Document } from 'react-pdf'; import { DocumentCallback, Options, PageCallback } from 'react-pdf/dist/cjs/shared/types'; import { pdfjs } from 'react-pdf'; import 'react-pdf/dist/Page/TextLayer.css'; import styles from './App.module.css'; pdfjs.GlobalWorkerOptions.workerSrc = `//unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.js`; interface PDFPreviewProps { curPdfUrl: string; } const App: React.FC<PDFPreviewProps> = React.memo(({ curPdfUrl }) => { const [totalPages, setTotalPages] = useState<number>(0); const canvasArray = useRef<Array<React.MutableRefObject<HTMLCanvasElement | null>>>([]); const handlePageRenderSuccess = (page: PageCallback) => {}; const options: Options = { cMapUrl: `https://cdn.jsdelivr.net/npm/pdfjs-dist@${pdfjs.version}/cmaps/`, httpHeaders: { 'Authorization': 'Bearer ' + getAccessToken() // 替换为实际的token获取逻辑 } }; const docuemtLocadSuccess = (pdf: DocumentCallback) => { setTotalPages(pdf.numPages); }; const updateRefArray = (index: number, element: HTMLCanvasElement | null) => { if (element) { canvasArray.current[index] = { current: element }; } }; const handlePageChange = (page: any) => {}; const renderPages = (totalPageNum: number | undefined) => { if (!totalPageNum || totalPageNum < 1) return null; const tagList: JSX.Element[] = []; for (let i = 1; i <= totalPageNum; i++) { tagList.push( <Page key={i} scale={1} onLoad={handlePageChange} canvasRef={(element) => updateRefArray(i, element)} onChange={handlePageChange} onRenderSuccess={handlePageRenderSuccess} pageNumber={i} /> ); } return tagList; }; return ( <div className={styles.container}> <div className={styles.rightPart}> <Document options={options} file={curPdfUrl} onLoadSuccess={docuemtLocadSuccess} > {renderPages(totalPages)} </Document> </div> </div> ); }, (prevProps, nextProps) => { // URL相同时不重新渲染,不同时触发重新渲染 return prevProps.curPdfUrl === nextProps.curPdfUrl; }); export default App;
内容的提问来源于stack exchange,提问作者Dolphin
相关产品推荐
相关产品推荐

