如何Implement Infinite Scroll:Claude Code 实战指南
学习如何implement infinite scroll:Claude Code 实战. 包含实用代码示例和分步指导。
無限滚动の仕組み
無限滚动は、用户が页面末尾に近づくと自動的に下一个数据を読み込むUIパターンです。SNSやニュース应用でおなじみですが、性能やアクセシビリティを考慮した实现は意外と難しいものです。使用 Claude Code,最佳实践に沿った实现を快速构建可以。
Intersection Observer使用…的基本实现
> Intersection Observer使用...的無限滚动のReactフックを作って。
> ローディング状態と错误处理も含めて。
import { useEffect, useRef, useState, useCallback } from 'react';
interface UseInfiniteScrollOptions<T> {
fetchFn: (page: number) => Promise<{ data: T[]; hasMore: boolean }>;
initialPage?: number;
threshold?: number;
}
function useInfiniteScroll<T>({ fetchFn, initialPage = 1, threshold = 0.8 }: UseInfiniteScrollOptions<T>) {
const [items, setItems] = useState<T[]>([]);
const [page, setPage] = useState(initialPage);
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true);
const [error, setError] = useState<Error | null>(null);
const observerRef = useRef<IntersectionObserver | null>(null);
const lastElementRef = useCallback(
(node: HTMLElement | null) => {
if (loading) return;
if (observerRef.current) observerRef.current.disconnect();
observerRef.current = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting && hasMore) {
setPage((prev) => prev + 1);
}
},
{ threshold }
);
if (node) observerRef.current.observe(node);
},
[loading, hasMore, threshold]
);
useEffect(() => {
let cancelled = false;
setLoading(true);
setError(null);
fetchFn(page)
.then((result) => {
if (!cancelled) {
setItems((prev) => [...prev, ...result.data]);
setHasMore(result.hasMore);
}
})
.catch((err) => { if (!cancelled) setError(err); })
.finally(() => { if (!cancelled) setLoading(false); });
return () => { cancelled = true; };
}, [page, fetchFn]);
return { items, loading, hasMore, error, lastElementRef };
}
组件での使用例
function ArticleList() {
const fetchArticles = useCallback(async (page: number) => {
const res = await fetch(`/api/articles?page=${page}&limit=20`);
const data = await res.json();
return { data: data.articles, hasMore: data.hasMore };
}, []);
const { items, loading, hasMore, error, lastElementRef } = useInfiniteScroll({
fetchFn: fetchArticles,
});
return (
<div role="feed" aria-busy={loading} aria-label="記事一覧">
{items.map((article, index) => (
<article
key={article.id}
ref={index === items.length - 1 ? lastElementRef : null}
aria-posinset={index + 1}
aria-setsize={hasMore ? -1 : items.length}
>
<h2>{article.title}</h2>
<p>{article.summary}</p>
</article>
))}
{loading && <div aria-live="polite">読み込み中...</div>}
{error && <div role="alert">エラーが発生しました。再試行してください。</div>}
{!hasMore && <p>すべての記事を表示しました。</p>}
</div>
);
}
后端のカーソルベース分页
// オフセットベースよりもカーソルベースの方が性能が良い
app.get('/api/articles', async (req, res) => {
const limit = Math.min(Number(req.query.limit) || 20, 100);
const cursor = req.query.cursor as string | undefined;
const where = cursor ? { id: { gt: cursor } } : {};
const articles = await prisma.article.findMany({
where,
take: limit + 1,
orderBy: { id: 'asc' },
});
const hasMore = articles.length > limit;
const data = hasMore ? articles.slice(0, -1) : articles;
res.json({
data,
hasMore,
nextCursor: hasMore ? data[data.length - 1].id : null,
});
});
总结
借助 Claude Code,Intersection Observerベースの無限滚动からカーソルベースAPIまで一貫して构建可以。大量数据の描画性能には仮想滚动实现を、分页方式の比較は分页实现。
Intersection Observer API的详细信息请参阅MDN Web Docs。
免费 PDF:5 分钟看懂 Claude Code 速查表
只需留下邮箱,我们就会立即把这份 A4 一页速查表 PDF 发送给你。
我们会严格保护你的个人信息,绝不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
本文作者
Masa
深度使用 Claude Code 的工程师。运营 claudecode-lab.com——一个涵盖 10 种语言、超过 2,000 页内容的科技媒体。
相关文章
Claude Code 的 7 个 CLAUDE.md 模板 | 可以直接复制到真实项目
面向个人应用、内容站、API、团队仓库和遗留代码库的 7 个实用 CLAUDE.md 模板,附常见失败案例。
Claude Code Approval 与 Sandbox 指南 | 日常安全使用的实战设置
用可直接参考的 settings、hooks、失败案例与流程示例,讲清楚 Claude Code 的 allow / ask / deny / sandbox 应该怎么分。
Claude Code 完全入门指南 2026 | 从零到实战应用的 7 个步骤
专为 Claude Code 新手打造的完整入门指南。从安装到融入真实开发工作流——涵盖 Masa 刚开始使用时踩过的所有坑。