Communitygithub.com

YepAPI/skills

基於游標的分頁、IntersectionObserver、使用 @tanstack/react-virtual 的虛擬化列表,以及滾動位置恢復。

skills 是什麼?

此技能提供了一套完整的解決方案,用於在 React 應用程式中實現高效、無縫的無限滾動功能。它針對大型列表(例如超過 1,000 個項目)進行了優化,透過結合基於游標的分頁、IntersectionObserver 進行預取,以及使用 `@tanstack/react-virtual` 進行 DOM 虛擬化,顯著提升了渲染效能和使用者體驗。該技能涵蓋了從資料擷取模式(使用 React Query 的 `useInfiniteQuery`)到 UI 狀態(如載入骨架、空狀態、列表結尾提示)的所有方面,並提供了常見陷阱的規避指南。它支援包括 Claude Code、Cursor 和 Codex 在內的主流 AI 代理平台,開發者可直接將其整合到專案中,快速建立流暢的無限滾動體驗。

相容平台~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/YepAPI/skills/tree/HEAD/skills/infinite-scroll

在你喜歡的 AI 中提問

開啟一個已預先載入此 Agent Skill 的新對話。

說明文件

無限滾動

規則

  • 基於游標的分頁:使用最後一個項目的 ID 或時間戳作為游標 — 不要使用偏移量,偏移量會在插入或刪除時產生問題
  • IntersectionObserver:附加到列表底部的哨兵元素 — 當可見時觸發資料擷取
  • 虛擬化:對於超過 1,000 個項目的列表,使用 @tanstack/react-virtual — 僅在 DOM 中渲染可見的行
  • 載入骨架:在擷取下一頁時,在列表底部顯示佔位項目 — 不要使用旋轉圖示
  • 「載入更多」按鈕:提供作為偏好手動控制或網路連線較慢的使用者的備用方案
  • 滾動位置恢復:在導航前儲存滾動位置,返回時恢復 — 使用 sessionStorage 或路由狀態
  • 空狀態:當列表為空時顯示訊息 — 不要顯示空白頁面
  • 列表結尾:當 hasNextPage 為 false 時顯示「您已到達結尾」 — 停止觀察器

模式

// 使用 React Query + IntersectionObserver 的無限滾動
import { useInfiniteQuery } from "@tanstack/react-query";
import { useRef, useEffect } from "react";

function InfiniteList() {
  const sentinelRef = useRef<HTMLDivElement>(null);
  const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteQuery({
    queryKey: ["items"],
    queryFn: ({ pageParam }) => fetchItems({ cursor: pageParam, limit: 20 }),
    getNextPageParam: (lastPage) => lastPage.nextCursor ?? undefined,
    initialPageParam: undefined as string | undefined,
  });

  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => { if (entries[0].isIntersecting && hasNextPage) fetchNextPage(); },
      { rootMargin: "200px" } // 在使用者到達底部之前預先擷取
    );
    if (sentinelRef.current) observer.observe(sentinelRef.current);
    return () => observer.disconnect();
  }, [hasNextPage, fetchNextPage]);

  const allItems = data?.pages.flatMap((page) => page.items) ?? [];

  return (
    <div>
      {allItems.map((item) => <ItemCard key={item.id} item={item} />)}
      {isFetchingNextPage && <LoadingSkeleton count={3} />}
      {!hasNextPage && allItems.length > 0 && <p>您已到達結尾</p>}
      {allItems.length === 0 && !isFetchingNextPage && <EmptyState />}
      <div ref={sentinelRef} />
    </div>
  );
}

// 用於 10k+ 項目的虛擬化列表
import { useVirtualizer } from "@tanstack/react-virtual";

function VirtualList({ items }: { items: Item[] }) {
  const parentRef = useRef<HTMLDivElement>(null);
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 64, // 估計的行高(像素)
    overscan: 5,
  });

  return (
    <div ref={parentRef} style={{ height: "600px", overflow: "auto" }}>
      <div style={{ height: `${virtualizer.getTotalSize()}px`, position: "relative" }}>
        {virtualizer.getVirtualItems().map((virtualRow) => (
          <div key={virtualRow.key} style={{
            position: "absolute", top: 0, left: 0, width: "100%",
            transform: `translateY(${virtualRow.start}px)`,
          }}>
            <ItemCard item={items[virtualRow.index]} />
          </div>
        ))}
      </div>
    </div>
  );
}

應避免

  • 基於偏移量的分頁 — 插入或刪除會導致所有偏移量移位,造成重複或遺失項目
  • 載入所有項目後再在客戶端進行過濾 — 僅從伺服器擷取當前頁面
  • 渲染超過 10k 個 DOM 節點 — 使用 @tanstack/react-virtual 僅渲染可見行
  • IntersectionObserver 未設定 rootMargin — 添加 100-200 像素的邊距,以便在使用者到達底部之前預先擷取
  • 缺少列表結尾指示器 — 使用者需要知道沒有更多內容可以載入

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

YepAPI/skills

WCAG 2.1 AA — semantic HTML, keyboard navigation, screen readers.

YepAPI/skills

Experiment setup, variant splitting, statistical significance, and feature flag integration.

YepAPI/skills

CRUD generators, data tables, user management, role-based access, and bulk operations.

YepAPI/skills

Monitor what ChatGPT and Gemini say about your brand using YepAPI.

YepAPI/skills

Queue workers with BullMQ/Inngest/Trigger.dev, job retries, dead letter queues, concurrency.

YepAPI/skills

Commander.js, interactive prompts with Clack, spinners, colors, config files, exit codes, and npm publishing.

YepAPI/skills

cmdk integration, Cmd+K trigger, fuzzy search, grouped actions, keyboard navigation, and dynamic action registration.

YepAPI/skills

Content planning with trend and sentiment data from YepAPI.

YepAPI/skills

Content calendar, keyword-driven planning, and content briefs.

YepAPI/skills

Theme switching with CSS variables, system preference detection, next-themes, and persistence.

YepAPI/skills

Threaded comments, @mentions, reactions, moderation queue, optimistic UI, and cursor pagination.

YepAPI/skills

Competitive analysis between domains using YepAPI.

YepAPI/skills

Layout grids, KPI cards, data tables with sorting/filtering, sidebar navigation, and responsive breakpoints.

YepAPI/skills

Data encryption — TLS everywhere, AES-256-GCM for PII, bcrypt passwords, encrypted backups.

YepAPI/skills

Chart patterns with Recharts/Nivo/D3, responsive containers, color scales, and accessibility.

YepAPI/skills

Drizzle schema definition, push vs migrate, type-safe queries, relations, transactions, and prepared statements.

YepAPI/skills

Upload handling, resize/compress, CDN delivery, blur placeholders, and next/image patterns.

YepAPI/skills

Local business and Google Maps research using YepAPI.

YepAPI/skills

Tenant isolation (row-level vs schema), subdomain routing, per-tenant config, and data partitioning.

YepAPI/skills

Chat UI components, streaming responses with AI SDK, context window management, and RAG patterns.

相關技能