无限滚动(Infinite Scroll)是一种常见的加载方式,适用于长列表数据,如社交媒体流、商品列表等。为了提升性能,我们需要一个高效的解决方案,避免一次性渲染过多 DOM 节点。
本文介绍如何使用 React 和 IntersectionObserver API 封装一个高性能的无限滚动组件。
需求分析
- 懒加载数据:滚动到底部时自动加载。
- 性能优化:减少 DOM 渲染,降低内存占用。
- 支持容器滚动:支持窗口和容器滚动。
- 兼容性好:使用现代 API,兼容性良好。
核心实现
1. 创建 useInfiniteScroll
Hook
import { useEffect, useRef } from "react";
type Props = {
loadMore: () => void;
hasMore: boolean;
};
export const useInfiniteScroll = ({ loadMore, hasMore }: Props) => {
const ref = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (!hasMore || !ref.current) return;
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) loadMore();
});
observer.observe(ref.current);
return () => observer.disconnect();
}, [loadMore, hasMore]);
return ref;
};
2. 封装 InfiniteScroll
组件
import React from "react";
import { useInfiniteScroll } from "./useInfiniteScroll";
type Props<T> = {
items: T[];
renderItem: (item: T) => JSX.Element;
loadMore: () => void;
hasMore: boolean;
};
export const InfiniteScroll = <T,>({ items, renderItem, loadMore, hasMore }: Props<T>) => {
const sentinelRef = useInfiniteScroll({ loadMore, hasMore });
return (
<div>
{items.map(renderItem)}
{hasMore && <div ref={sentinelRef} style={{ height: 1 }} />}
</div>
);
};
3. 使用示例
import React, { useState } from "react";
import { InfiniteScroll } from "./InfiniteScroll";
const fetchMockData = async (page: number) =>
new Promise<number[]>((resolve) => setTimeout(() => resolve(Array.from({ length: 10 }, (_, i) => page * 10 + i)), 1000));
export const Demo = () => {
const [items, setItems] = useState<number[]>([]);
const [page, setPage] = useState(0);
const [hasMore, setHasMore] = useState(true);
const loadMore = async () => {
const newData = await fetchMockData(page + 1);
setItems((prev) => [...prev, ...newData]);
setPage(page + 1);
setHasMore(newData.length > 0);
};
return (
<InfiniteScroll
items={items}
renderItem={(item) => <div key={item} style={{ padding: 10, borderBottom: "1px solid #ddd" }}>{item}</div>}
loadMore={loadMore}
hasMore={hasMore}
/>
);
};
性能优化
- 使用 Intersection Observer 替代
onscroll
事件监听,提高性能。 - 减少 re-render:
React.memo
避免不必要的组件更新。 - 支持虚拟列表(可选):结合
react-window
提高渲染性能。
结语
本文介绍了如何封装一个高性能的 React 无限滚动组件,使用 useInfiniteScroll
Hook 结合 Intersection Observer 监听滚动,同时进行了性能优化。
你可以根据需求扩展组件,例如上拉刷新、自定义加载动画等。如果觉得有帮助,欢迎点赞、收藏、交流!🚀