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

如何为从API拉取数据的React组件制作加载器,避免图片加载时布局错乱

实现方案

你的实现思路是可行的,我们可以在AlbumsList组件内新增加载状态,监听所有卡片关联图片的加载完成事件,全部资源加载完成后再隐藏加载Spinner,同时支持后续分批加载新数据时自动重置加载状态,适配非一次性加载的场景。

实现步骤
  • 给组件添加两个状态:isLoading 控制Spinner显隐,loadedImagesCount 统计已加载完成的图片数量
  • 给AlbumCard组件传入图片加载完成的回调函数,每一张图片加载完成后触发计数+1
  • 当已加载计数等于当前渲染的卡片总数时,将isLoading设为false,隐藏Spinner
  • Spinner用绝对定位放在列表容器的居中位置,加载阶段可以给列表加半透明遮罩或者占位样式,避免布局跳动
改造后完整代码

AlbumsList组件代码

import { useState, useEffect } from "react";
import AlbumCard from "../Cards/AlbumCard";
// 替换为你项目内的Spinner组件即可
import Spinner from "./Spinner";

const AlbumsList = (props) => {
  const [isLoading, setIsLoading] = useState(true);
  const [loadedImagesCount, setLoadedImagesCount] = useState(0);
  // 总卡片数量,随props.item更新自动同步
  const totalCount = props.item?.length || 0;

  // 单张图片加载完成的回调
  const handleImageLoad = () => {
    setLoadedImagesCount(prev => {
      const newCount = prev + 1;
      // 所有图片加载完成时关闭加载状态
      if (newCount >= totalCount) {
        setIsLoading(false);
      }
      return newCount;
    });
  };

  // 分批加载新数据时自动重置加载状态
  useEffect(() => {
    setIsLoading(true);
    setLoadedImagesCount(0);
  }, [props.item]);

  if (!totalCount) return null;

  return (
    <div style={{ position: "relative", width: "100%" }}>
      {/* 加载中显示Spinner */}
      {isLoading && (
        <div style={{
          position: "absolute",
          top: "50%",
          left: "50%",
          transform: "translate(-50%, -50%)",
          zIndex: 10,
        }}>
          <Spinner />
        </div>
      )}
      <ul
        style={{
          position: "relative",
          padding: "0",
          display: "flex",
          flexWrap: "wrap",
          width: "100%",
          opacity: isLoading ? 0.3 : 1,
          transition: "opacity 0.2s ease",
        }}
      >
        {props.item.map((item) => (
          <AlbumCard
            id={item.id}
            key={item.id}
            image={item.picture}
            artist={item.name}
            title={item.title}
            onImageLoad={handleImageLoad}
          />
        ))}
      </ul>
    </div>
  )
}

export default AlbumsList

AlbumCard组件修改说明

需要在AlbumCard内部的img标签上绑定onLoad事件,触发传入的回调即可,示例如下:

// AlbumCard组件内部img标签修改示例
<img 
  src={image} 
  alt={`${title} - ${artist}`}
  onLoad={onImageLoad}
  onError={onImageLoad}
  // 提前设置宽高可进一步避免布局偏移
  width={200}
  height={200}
  style={{ objectFit: "cover" }}
/>
优化方案建议
  • 可以给AlbumCard提前设置固定宽高,或者使用卡片骨架屏占位,加载阶段显示骨架屏,比单纯显示Spinner的用户体验更好
  • 如果图片资源体积较大,可添加图片懒加载逻辑,仅加载可视区域内的图片,提升页面加载速度
  • 上述代码中已给img添加了onError事件也触发加载回调,避免单张图片加载失败导致Spinner一直显示的问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 05:54:06