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

在React useEffect中创建与释放Blob的正确方式及无限循环解决

问题分析与解决

原代码触发无限循环的核心原因是useEffect的依赖数组包含了src:每次setSrc(url)更新src状态后,useEffect会因为依赖变化重新执行,再次调用getImage、创建新的ObjectURL并更新src,形成无限循环。同时清理函数依赖src的闭包值,可能无法正确释放最新的URL。

正确实现方式

import { useEffect, useState, type ReactElement } from 'react';

async function getImage(): Promise<Blob> {
  const res = await fetch('https://picsum.photos/200');
  const blob = await res.blob();
  return blob;
}

export default function Image(): ReactElement {
  const [src, setSrc] = useState<string | null>(null);

  useEffect(() => {
    let objectUrl: string | null = null;

    const fetchAndSetImage = async () => {
      try {
        const blob = await getImage();
        objectUrl = URL.createObjectURL(blob);
        setSrc(objectUrl);
      } catch (error) {
        console.error('图片加载失败:', error);
      }
    };

    fetchAndSetImage();

    // 组件卸载或effect重新执行时清理
    return () => {
      if (objectUrl) {
        URL.revokeObjectURL(objectUrl);
      }
    };
  }, []); // 空依赖数组,仅在组件挂载时执行一次

  return <img src={src ?? undefined} alt="some image" />;
}

关键改动说明

  • 清空依赖数组:[]确保useEffect只在组件挂载时执行一次,避免因src更新触发重复执行。
  • 内部保存ObjectURL:在effect内部声明objectUrl变量,直接捕获创建的URL,避免闭包导致的旧值问题,确保清理时能正确释放资源。
  • 异步函数封装:把异步逻辑封装成内部函数,更清晰且方便错误处理。
  • 合理的错误处理:用console.error替代直接throw,避免未捕获异常导致程序崩溃。

最佳实践补充

  • 如果需要根据props变化重新加载图片(比如图片地址动态变化),把对应的props加入依赖数组即可,同时清理函数会自动释放旧的URL。
  • 始终确保每个URL.createObjectURL都对应一个URL.revokeObjectURL,避免浏览器内存泄漏。
  • 若图片加载是高频操作(比如列表中大量图片),可以考虑缓存Blob或ObjectURL,减少重复请求和资源创建。

内容的提问来源于stack exchange,提问作者Noah

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 19:54:59