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

如何结合useInfinityQuery与Directus实现无限滚动?

在Directus集合上用TanStack Query实现无限滚动的实操方案

前置准备

确保你已经安装了@tanstack/react-query和Directus官方SDK(@directus/sdk),并且已配置好Directus客户端实例。

核心实现逻辑

用TanStack Query的useInfiniteQuery钩子,结合Directus的limit(每页条数)和offset(偏移量)分页参数分批拉取数据。每次触发下一页请求时,把offset设为上一页数据的总条数,以此实现无限滚动。

完整组件示例

import { useInfiniteQuery } from '@tanstack/react-query';
import { createDirectus, rest, readItems } from '@directus/sdk';
import { useEffect, useRef, useState } from 'react';

// 初始化Directus客户端
const directus = createDirectus('你的Directus实例地址').with(rest());

// 封装拉取集合数据的函数
const fetchCollectionItems = async ({ pageParam = 0 }) => {
  const pageSize = 10; // 每页加载10条,可按需调整
  const response = await directus.request(
    readItems('你的集合名称', {
      limit: pageSize,
      offset: pageParam,
      // 可选:添加排序、过滤、指定返回字段等配置
      sort: ['-created_at'],
      fields: ['id', 'title', 'description', 'cover_image']
    })
  );

  return {
    items: response,
    nextOffset: pageParam + pageSize,
    hasMore: response.length === pageSize // 返回条数等于每页大小,说明还有更多数据
  };
};

export const InfiniteScrollCardList = () => {
  const {
    data,
    fetchNextPage,
    hasNextPage,
    isLoading,
    isError,
    error
  } = useInfiniteQuery({
    queryKey: ['collection-items'],
    queryFn: fetchCollectionItems,
    getNextPageParam: (lastPage) => {
      // 有更多数据就返回下一页偏移量,否则返回undefined终止分页
      return lastPage.hasMore ? lastPage.nextOffset : undefined;
    }
  });

  const loadMoreTrigger = useRef(null);
  const [isFetchingNext, setIsFetchingNext] = useState(false);

  // 用Intersection Observer监听加载触发元素
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        const entry = entries[0];
        if (entry.isIntersecting && hasNextPage && !isFetchingNext) {
          setIsFetchingNext(true);
          fetchNextPage().finally(() => setIsFetchingNext(false));
        }
      },
      { threshold: 0.1 }
    );

    if (loadMoreTrigger.current) observer.observe(loadMoreTrigger.current);
    return () => {
      if (loadMoreTrigger.current) observer.unobserve(loadMoreTrigger.current);
    };
  }, [hasNextPage, fetchNextPage, isFetchingNext]);

  // 处理基础状态
  if (isLoading) return <div>加载中...</div>;
  if (isError) return <div>加载失败:{(error as Error).message}</div>;

  return (
    <div className="card-list">
      {/* 渲染所有已加载的卡片 */}
      {data?.pages.map((page, pageIdx) => (
        <React.Fragment key={pageIdx}>
          {page.items.map((item) => (
            <div key={item.id} className="card">
              <h3>{item.title}</h3>
              <p>{item.description}</p>
              {item.cover_image && (
                <img 
                  src={`${directus.url}/assets/${item.cover_image}`} 
                  alt={item.title} 
                />
              )}
            </div>
          ))}
        </React.Fragment>
      ))}

      {/* 加载触发元素 */}
      {hasNextPage && (
        <div ref={loadMoreTrigger} className="load-more">
          {isFetchingNext ? '加载更多中...' : '滚动加载更多'}
        </div>
      )}

      {!hasNextPage && <div>已加载全部内容</div>}
    </div>
  );
};

关键细节说明

  • Directus分页规则:offset从0开始计数,每次请求的偏移量等于上一页偏移量+每页大小;当返回数据条数小于每页大小时,说明没有更多数据。
  • 滚动监听优化:用Intersection Observer替代传统scroll事件,更高效且能精准检测元素进入视口的时机。
  • TanStack Query优势:自动处理数据缓存、重复请求拦截、加载/错误状态管理,无需手动维护这些逻辑。
  • 权限注意:确保你的Directus账号对目标集合有read权限,否则会返回权限错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 04:16:18