You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何缓存Axios请求的二维码,避免模态框重开重复请求?

解决模态框二维码缓存问题

问题根源

  1. useMemo+async的问题:async函数本身返回Promise,所以qrc变量实际是Promise对象,无法直接作为img的src属性值。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 09:30:56