rc-virtual-list使用出现bug

37 阅读2分钟

背景:

页面做了rem自适应,会根据屏幕宽度自动调整html中的fontSize; 所以同接口返回同一组数据不同屏幕宽度会显示不同高度;

在设计稿尺寸下,如果接口返回数据有20条,虚拟盒子高度会被撑开而滚动,滚动时可以加载更多数据,这时候功能正常;

但是!!!在屏幕宽度比较小时,20条数据加载完还不能撑满盒子高度,导致虚拟列表没法滚动,加载更多,造成了事故!!!

image.png

image.png

这种问题应该还挺容易出现的,渲染不定高的虚拟列表,有时候确实会造成盒子不被撑满情况~

解决方式

(1)一次性获取多点数据,比如50条,保证盒子始终能被撑开(打补丁)

(2)组件中传入的高度ContainerHeight改成动态高度ContainerHeight * scale(这里的scale是动态的比例),根据屏幕大小变化

自己写一个下拉加载更多组件

使用# IntersectionObserver这个api

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

export default function IntersectionObserverDemo() {
  const [items, setItems] = useState(Array.from({ length: 20 }));
  const [hasMore, setHasMore] = useState(true);
  const loaderRef = useRef(null);

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        if (entries[0].isIntersecting && hasMore) {
          // 模拟加载更多数据
          setTimeout(() => {
            setItems(prevItems => {
              const newItems = [...prevItems, ...Array.from({ length: 20 })];
              if (newItems.length >= 100) setHasMore(false);
              return newItems;
            });
          }, 1000);
        }
      },
      { threshold: 1.0 } // 当 loader 元素完全进入视口时触发
    );

    if (loaderRef.current) {
      observer.observe(loaderRef.current);
    }

    return () => {
      if (loaderRef.current) {
        observer.unobserve(loaderRef.current);
      }
    };
  }, [hasMore]);

  return (
    <div>
      <h1>使用 Intersection Observer</h1>
      {items.map((_, index) => (
        <div key={index} style={{ padding: '20px', border: '1px solid #ccc' }}>
          项目 #{index + 1}
        </div>
      ))}
      {hasMore ? (
        <div ref={loaderRef} style={{ textAlign: 'center', padding: '20px' }}>
          加载更多...
        </div>
      ) : (
        <div style={{ textAlign: 'center', padding: '20px' }}>没有更多内容了</div>
      )}
    </div>
  );
}

实现一个图片懒加载功能

import { useRef, useEffect } from 'react';

function LazyImage({ src, alt }) {
  const imgRef = useRef();

  useEffect(() => {
    const observer = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target;
          img.src = img.getAttribute('data-src');
          observer.unobserve(img);
        }
      });
    });

    if (imgRef.current) {
      observer.observe(imgRef.current);
    }

    return () => {
      if (imgRef.current) {
        observer.unobserve(imgRef.current);
      }
    };
  }, []);

  return <img ref={imgRef} data-src={src} alt={alt} />;
}

遗留其他待解决问题

对于服务端渲染的页面 如果做了rem自适应, 每次加载完客户端代码后,都会闪一下,因为html的fontSize可能会重新被赋值,感觉这种体验很不好,暂时没有找到比较好的解决方式。