如何缓存Axios请求的二维码,避免模态框重开重复请求?
解决模态框二维码缓存问题
问题根源
- useMemo+async的问题:async函数本身返回Promise,所以
qrc变量实际是Promise对象,无法直接作为img的src属性值。 - useEffect+useState的问题:因为模态框通过
{showModal && <Modal />}控制渲染,每次打开模态框时,Modal组件会重新挂载,内部state被重置,导致每次都会发起新请求。
解决方案一:状态提升到父组件(推荐)
把二维码的缓存状态放在父组件中,这样即使Modal组件卸载,缓存的二维码依然保留,下次打开模态框时直接复用。
父组件代码
import { useState } from 'react'; function ParentComponent() { const [showModal, setShowModal] = useState(false); // 缓存二维码的状态 const [cachedQrCode, setCachedQrCode] = useState(''); const url = window.location.href; // 仅当缓存为空时发起请求 const fetchQrCode = async () => { if (!cachedQrCode) { const res = await getQRCode(url); setCachedQrCode(res.data); } }; // 打开模态框时触发请求(若未缓存) const openModal = () => { fetchQrCode(); setShowModal(true); }; return ( <> <button onClick={openModal}>打开模态框</button> {showModal && ( <Modal qrCode={cachedQrCode} onClose={() => setShowModal(false)} /> )} </> ); }
Modal组件代码
function Modal({ qrCode, onClose }) { return ( <div className="modal"> <button onClick={onClose}>关闭</button> {qrCode ? ( <img src={qrCode} alt="二维码" /> ) : ( <div>加载中...</div> )} </div> ); }
解决方案二:使用useRef+缓存标记(适合非条件渲染的模态框)
如果模态框通过display样式控制显示隐藏(而非条件渲染卸载组件),可以在Modal内部用useRef记录是否已获取过二维码,避免重复请求:
import { useState, useEffect, useRef } from 'react'; function Modal() { const [qrc, setQrc] = useState(''); const [isLoading, setIsLoading] = useState(false); const url = window.location.href; // 记录是否已经成功获取过二维码 const hasFetched = useRef(false); useEffect(() => { // 仅当未获取过且无缓存时请求 if (!hasFetched.current && !qrc) { setIsLoading(true); getQRCode(url) .then(res => { setQrc(res.data); hasFetched.current = true; }) .finally(() => { setIsLoading(false); }); } }, [url]); return ( <div className="modal"> <button onClick={() => {/* 关闭逻辑 */}}>关闭</button> {isLoading ? ( <div>加载中...</div> ) : qrc ? ( <img src={qrc} alt="二维码" /> ) : null} </div> ); }
补充:修复useMemo的错误用法
如果一定要用useMemo,需要配合状态管理处理Promise结果,但无法解决组件重新挂载时重复请求的问题,仅作参考:
import { useState, useEffect, useMemo } from 'react'; function Modal() { const [qrc, setQrc] = useState(''); const [isLoading, setIsLoading] = useState(false); const url = window.location.href; // 用useMemo缓存请求Promise(仅url变化时重新创建) const qrPromise = useMemo(() => getQRCode(url), [url]); useEffect(() => { setIsLoading(true); qrPromise .then(res => setQrc(res.data)) .finally(() => setIsLoading(false)); }, [qrPromise]); return ( <div className="modal"> {isLoading ? <div>加载中...</div> : <img src={qrc} alt="二维码" />} </div> ); }
内容的提问来源于stack exchange,提问作者Grambam
相关产品推荐
相关产品推荐

