如何在react-pdf库中避免同一用户会话重复发起PDF文件API请求
React会话内避免重复请求PDF的实现方案
下面是几种实用的实现方式,根据你的场景选择:
1. 内存级缓存(组件/全局共享)
适合单组件内或全局会话内的临时缓存,页面刷新后缓存会清空。可以用useRef(不触发重渲染)或者全局对象来存储:
import { useRef, useState } from 'react'; const PdfViewer = ({ pdfId }) => { const pdfCache = useRef({}); // 用useRef存缓存,避免重渲染 const [pdfData, setPdfData] = useState(null); const [loading, setLoading] = useState(false); const fetchPdf = async () => { // 先检查缓存是否存在 if (pdfCache.current[pdfId]) { setPdfData(pdfCache.current[pdfId]); return; } setLoading(true); try { const res = await fetch(`/api/pdf/${pdfId}`); const blob = await res.blob(); // 存入缓存 pdfCache.current[pdfId] = blob; setPdfData(blob); } catch (err) { console.error('PDF请求失败:', err); } finally { setLoading(false); } }; return ( <div> <button onClick={fetchPdf} disabled={loading}>加载PDF</button> {pdfData && <iframe src={URL.createObjectURL(pdfData)} width="100%" height="600px" />} </div> ); };
如果需要全局跨组件共享缓存,可以在组件外定义一个全局缓存对象,或者用React Context传递缓存实例。
2. SessionStorage缓存(跨页面/刷新保留)
如果需要在同一会话的不同页面或刷新后依然保留缓存,可以用sessionStorage,它的存储周期和用户会话一致:
const fetchPdf = async (pdfId) => { const cacheKey = `pdf_${pdfId}`; // 先从sessionStorage读取缓存 const cachedBase64 = sessionStorage.getItem(cacheKey); if (cachedBase64) { // 将base64转回Blob对象 const byteChars = atob(cachedBase64); const byteNumbers = Array.from(byteChars, char => char.charCodeAt(0)); const byteArray = new Uint8Array(byteNumbers); return new Blob([byteArray], { type: 'application/pdf' }); } // 无缓存时请求API const res = await fetch(`/api/pdf/${pdfId}`); const blob = await res.blob(); // 将Blob转成base64存入sessionStorage const reader = new FileReader(); reader.onloadend = () => { const base64Data = reader.result.split(',')[1]; sessionStorage.setItem(cacheKey, base64Data); }; reader.readAsDataURL(blob); return blob; };
注意:sessionStorage有大小限制(通常5MB左右),大体积PDF不适合用这个方案。
3. 封装自定义Hook复用缓存逻辑
把缓存逻辑抽成自定义Hook,方便多个组件复用:
import { useRef, useCallback, useState } from 'react'; const usePdfCache = () => { const cache = useRef({}); const getPdf = useCallback(async (pdfId) => { // 优先取缓存 if (cache.current[pdfId]) { return cache.current[pdfId]; } // 请求并缓存 const res = await fetch(`/api/pdf/${pdfId}`); const blob = await res.blob(); cache.current[pdfId] = blob; return blob; }, []); return { getPdf }; }; // 使用示例 const PdfComponent = ({ pdfId }) => { const { getPdf } = usePdfCache(); const [pdfBlob, setPdfBlob] = useState(null); const handleLoad = async () => { const blob = await getPdf(pdfId); setPdfBlob(blob); }; return ( <> <button onClick={handleLoad}>加载PDF</button> {pdfBlob && <iframe src={URL.createObjectURL(pdfBlob)} width="100%" height="500px" />} </> ); };
关键注意点
- 缓存键唯一性:用PDF的唯一ID(比如后端返回的文件ID)作为缓存键,避免不同PDF互相覆盖。
- 缓存失效:如果PDF内容可能在会话内更新,可以给缓存条目添加时间戳,或者提供手动清空缓存的方法。
- 大文件处理:大体积PDF优先用内存缓存,避免
sessionStorage存储溢出。 - 错误处理:保留请求错误捕获逻辑,避免请求失败后返回无效缓存。
内容的提问来源于stack exchange,提问作者Vamsi Krishna Lakshmipuram
相关产品推荐
相关产品推荐

