React中动态切换PDF预览时页面滚动位置会重置怎么办?
在用react-pdf做动态PDF预览时遇到个怪问题:Viewer组件切换不同PDF文件后,页面总会强制跳到顶部。我试过用useState保存scrollTop值,但组件重新渲染后还是无效…
具体场景是点击不同文件时动态更新document状态,代码类似这样:
import { Document, Page, pdfjs } from 'react-pdf';
function PdfPreview({ selectedFile }) {
const [numPages, setNumPages] = useState(0);
const [pdfDoc, setPdfDoc] = useState(null);
useEffect(() => {
pdfjs.GlobalWorkerOptions.workerSrc = '//cdn.jsdelivr.net/npm/pdfjs-dist@3.4.120/build/pdf.worker.min.js';
const loadPdf = async () => {
const pdf = await pdfjs.getDocument(selectedFile).promise;
setPdfDoc(pdf);
setNumPages(pdf.numPages);
};
loadPdf();
}, [selectedFile]);
return (
<div>
{[...Array(numPages)].map((_, i) => (
<Page key={i} pageNumber={i + 1} />
))}
</div>
);
}
当切换selectedFile时整个预览区域重新加载,之前滚动到中间的位置就会消失。用window.scrollTo强行定位又会导致渲染不同步的问题…
你用useState保存scrollTop无效的原因是:状态保存的是值,但组件重新mount后DOM重新生成,滚动是针对具体的容器元素的,你需要在正确的时机把保存的值应用回去。
简单直接的解决方案是用useRef保存滚动容器的引用,在PDF加载完成恢复:
几个关键点:
滚动容器需要有个ref绑定,切换前用ref获取当前滚动位置并存到ref里(注意是ref不是state,因为不需要触发重新渲染)。然后在numPages变化(说明PDF加载完成)后,用requestAnimationFrame延迟执行scrollTop赋值,这样能确保Page组件已经渲染到DOM里了。
如果你的滚动是针对window的,把ref绑定去掉,直接用document.documentElement.scrollTop就行。
还有个小建议:pdfjs的workerSrc建议放到组件外部或者useEffect的依赖数组里只执行一次,不然每次selectedFile变化都会重复设置。
直接用这个:
要点是用了一个ref容器保存滚动状态,同时在加载新PDF时用loading占位符撑住高度。这样即使pdfDoc置空了,外层容器的滚动位置也不会丢。
记得给外层容器设置固定高度和overflow:auto,不然滚动行为会有问题。另外加了个isMounted避免异步回调里的setState导致内存泄漏,这在实际项目里很重要。
试了下这方案挺稳的,页面切换时滚动位置能正常保持。