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

React JS如何实现新图片完全加载后再替换更新显示

问题修复方案

核心要解决两个问题:一是图片未加载完成就被渲染导致逐行显示、切换有延迟;二是图片容器无固定尺寸,加载过程中高度变化引发布局偏移。


核心实现逻辑

  • 收到SSE推送的新图片URI时,不直接渲染到DOM,先通过内存中的Image对象预加载
  • 监听图片的onload事件,确认图片完全加载解码完成后,再更新状态替换当前展示的图片,保证切换无延迟、无半加载状态
  • 给图片外层容器设置固定宽高比,在图片加载前就预留好展示空间,彻底避免布局偏移
  • 每次收到新URI时中断上一个未完成的预加载任务,保证永远只展示最新的、最后推送的图片,避免旧图加载完成后覆盖新图

完整代码修改

1. 调整状态定义与SSE监听逻辑

替换原来的imageURIs状态,增加预加载引用避免内存泄漏:

import { useState, useEffect, useRef } from 'react';

// 组件内
const [currentDisplayImage, setCurrentDisplayImage] = useState(null);
const preloadImgRef = useRef(null);

useEffect(() => {
  const serverImageURIsEventSource = new EventSource(
    url,
    {withCredentials: false}
  );

  serverImageURIsEventSource.addEventListener('open', () => {
    console.log('SSE opened!');
  });

  serverImageURIsEventSource.addEventListener('message', (e) => {
    const newImageUri = e.data;
    // 清理上一个未完成的预加载任务
    if (preloadImgRef.current) {
      preloadImgRef.current.onload = null;
      preloadImgRef.current.onerror = null;
    }
    // 创建内存图片实例预加载
    const preloadImg = new Image();
    preloadImgRef.current = preloadImg;

    preloadImg.onload = () => {
      // 加载完成才更新展示状态
      setCurrentDisplayImage(newImageUri);
      preloadImgRef.current = null;
    };

    preloadImg.onerror = (err) => {
      console.error('图片加载失败: ', err);
      preloadImgRef.current = null;
    };

    preloadImg.src = newImageUri;
  });

  serverImageURIsEventSource.addEventListener('error', (e) => {
    console.error('Error: ',  e);
  });

  serverImageURIsEventSource.addEventListener('close', () => {
    console.log('SSE closed!');
    serverImageURIsEventSource.close();
  });

  return () => {
    // 组件卸载时清理预加载回调和SSE连接
    if (preloadImgRef.current) {
      preloadImgRef.current.onload = null;
      preloadImgRef.current.onerror = null;
    }
    serverImageURIsEventSource.close();
  };
}, [prompt]);

2. 调整渲染逻辑,修复布局偏移

给图片容器设置固定宽高比,移除loading="lazy"属性(手动预加载场景下lazy属性会干扰加载时机):

<ImageList cols={1} sx={{ width: '100%' }}>
  {currentDisplayImage && (
    <ImageListItem 
      key={currentDisplayImage}
      // 按照实际图片的尺寸比例设置aspectRatio,比如16:9、4:3、1:1
      sx={{ aspectRatio: '16/9', width: '100%' }}
    >
      <img
        src={currentDisplayImage}
        alt="实时推送图片"
        loading="eager"
        style={{
          width: '100%',
          height: '100%',
          // 选择适配方式:cover会裁剪填满容器,contain会完整展示图片留空白
          objectFit: 'cover'
        }}
      />
    </ImageListItem>
  )}
</ImageList>

注意事项

  • aspectRatio的值要和后端返回的图片实际宽高比一致,否则会出现图片拉伸或者裁剪不符合预期的问题
  • 如果图片尺寸不固定,可以给容器设置固定高度,同样能避免布局偏移
  • 预加载创建的Image对象一定要在组件卸载、收到新图片时清空回调,避免内存泄漏和状态误更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 06:27:27