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

如何避免React Hook重复执行?以usePublicKey为例

解决自定义Hook的单例执行问题

你当前的usePublicKey Hook每次被组件引用时都会重新执行密钥生成流程,原因是useRef创建的fetched是每个Hook实例独立的,无法跨实例共享状态。要实现类似Apollo useQuery的单例执行+缓存效果,可以通过模块级全局变量来维护共享状态和请求锁,具体实现如下:

问题代码分析

你的原代码中,每个usePublicKey实例都有自己的fetched ref,因此每次引用Hook时,!fetched.current都会为true,导致重复执行密钥生成(同时修正了代码中的笔误:k应为keys):

export const usePublicKey = () => {
  const [publicKey, setPublicKey] = useState()
  const fetched = useRef(false) // 每个Hook实例独立,无法共享

  useEffect(() => {
    const fetchKey = async () => {
      const keys = await window.crypto.subtle.generateKey(
        {
            name: 'RSASSA-PKCS1-v1_5',
            modulusLength: 2048,
            publicExponent: new Uint8Array([1, 0, 1]),
            hash: 'SHA-256',
        },
        true,
        ['sign', 'verify']
      )
      const _publicKey = await window.crypto.subtle.exportKey('jwk', keys.publicKey)
      setPublicKey(_publicKey )
    }
    if(!fetched.current) { 
      fetched.current = true 
      fetchKey()
    }
  },[])

  return { publicKey }
}

单例执行的解决方案

利用模块级全局变量(整个应用生命周期内唯一)来缓存公钥和请求状态,确保密钥生成流程只执行一次,所有Hook实例共享结果:

// 模块级全局缓存:整个应用唯一
let publicKeyCache = null;
// 缓存请求Promise,避免并发重复请求
let fetchKeyPromise = null;

export const usePublicKey = () => {
  // 初始化时直接用全局缓存的值
  const [publicKey, setPublicKey] = useState(publicKeyCache);

  useEffect(() => {
    // 已有缓存,直接更新本地状态
    if (publicKeyCache) {
      setPublicKey(publicKeyCache);
      return;
    }

    // 已有请求在进行,等待同一个Promise完成
    if (fetchKeyPromise) {
      fetchKeyPromise.then(key => setPublicKey(key));
      return;
    }

    // 首次发起请求,缓存Promise
    fetchKeyPromise = (async () => {
      try {
        const keys = await window.crypto.subtle.generateKey(
          {
            name: 'RSASSA-PKCS1-v1_5',
            modulusLength: 2048,
            publicExponent: new Uint8Array([1, 0, 1]),
            hash: 'SHA-256',
          },
          true,
          ['sign', 'verify']
        );
        const _publicKey = await window.crypto.subtle.exportKey('jwk', keys.publicKey);
        // 缓存结果到全局变量
        publicKeyCache = _publicKey;
        return _publicKey;
      } finally {
        // 请求完成后清空Promise标记
        fetchKeyPromise = null;
      }
    })();

    // 订阅请求结果,更新本地状态
    fetchKeyPromise.then(key => setPublicKey(key));

  }, []);

  return { publicKey };
};

方案说明

  1. 全局缓存变量:publicKeyCache存储生成好的公钥,所有Hook实例共享这个值;fetchKeyPromise用于标记当前是否有请求在进行,避免多个Hook同时发起重复请求。
  2. 并发处理:当多个组件同时调用usePublicKey时,后续调用会直接等待第一个请求的Promise,不会重复执行密钥生成。
  3. 状态同步:请求完成后,所有Hook实例都会通过setPublicKey更新本地状态,拿到最新的公钥。

内容的提问来源于stack exchange,提问作者Elliott de Launay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:15:17