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

React Photo Album无限滚动问题:新增图片后滚动位置重置求助

问题描述

我正在尝试为react-photo-album组件实现无限滚动功能。已通过在相册下方设置Intersection Observer实现滚动到底部时获取更多图片,但存在问题:当将新图片添加到状态并传入相册组件props后,组件会重新渲染并回到顶部。不想自行实现相册组件,因为难以处理不同尺寸、比例图片的响应式行列布局。

现有代码

import React, { useState, useCallback, useRef } from 'react';
import PhotoAlbum from 'react-photo-album';

const [images, setImages] = useState<ImageState[]>([]);

const lastImageObserver = useRef<IntersectionObserver>();

const lastImageElementRef = useCallback(
  (node: any) => {
    if (lastImageObserver.current) lastImageObserver.current.disconnect();
    lastImageObserver.current = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting) {
          console.log('last image is intersecting');
          if (currentPage < Math.ceil(totalImages / rowsPerPage)) {
            setCurrentPage((prevPage) => prevPage + 1);
          }
        }
      },
      {
        threshold: 0.1,
      }
    );
    if (node) lastImageObserver.current.observe(node);
  },
  [currentPage, rowsPerPage]
);

const fetchImages = useCallback(async () => {
  const result = await getPhotos(
    currentPage,
    rowsPerPage
  );
  const newImages = result.images;
  setImages((prev) => [...prev, ...newImages]);

}, [currentPage, totalImages]);


return (
  <div>
    <div>
      <PhotoAlbum
        layout="rows"
        photos={images}
      />
      <div
        ref={lastImageElementRef}
        style={{ minHeight: '1px' }}
      ></div>
    </div>
  </div>
)
解决方案

1. 保存并恢复全局滚动位置

在请求新图片前记录当前滚动位置,DOM更新完成后恢复该位置:

const fetchImages = useCallback(async () => {
  // 记录当前滚动位置
  const scrollPosition = window.scrollY || document.documentElement.scrollTop;
  
  const result = await getPhotos(currentPage, rowsPerPage);
  setImages(prev => [...prev, ...result.images]);
  
  // 延迟恢复滚动,确保相册DOM已更新
  setTimeout(() => {
    window.scrollTo(0, scrollPosition);
  }, 0);
}, [currentPage, totalImages]);

2. 监听最后一张图片而非底部占位div

将Intersection Observer的监听目标改为相册的最后一张图片,减少布局变化带来的干扰:

return (
  <PhotoAlbum
    layout="rows"
    photos={images}
    renderPhoto={({ photo, index }) => {
      // 仅给最后一张图片绑定监听ref
      const targetRef = index === images.length - 1 ? lastImageElementRef : undefined;
      return <img ref={targetRef} src={photo.src} alt={photo.alt} />;
    }}
  />
);

同时可以移除原来的底部占位div,避免额外的布局变动。

3. 针对局部滚动容器的处理

如果相册放在自定义滚动容器内,需要记录和恢复容器的滚动位置:

const albumContainerRef = useRef<HTMLDivElement>(null);

const fetchImages = useCallback(async () => {
  if (!albumContainerRef.current) return;
  
  const scrollTop = albumContainerRef.current.scrollTop;
  const result = await getPhotos(currentPage, rowsPerPage);
  
  setImages(prev => [...prev, ...result.images]);
  
  setTimeout(() => {
    if (albumContainerRef.current) {
      albumContainerRef.current.scrollTop = scrollTop;
    }
  }, 0);
}, [currentPage, totalImages]);

// 渲染时给滚动容器添加ref
return (
  <div 
    ref={albumContainerRef} 
    style={{ overflowY: 'auto', height: '100vh' }}
  >
    <PhotoAlbum layout="rows" photos={images} />
  </div>
);

4. 优化组件重渲染

确保PhotoAlbum组件仅在images变化时重渲染:

  • 检查父组件是否有不必要的重渲染,可通过React.memo包裹父组件或相关子组件
  • 确保fetchImages、lastImageElementRef等回调的依赖项正确,避免不必要的更新

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 12:27:27