React Query无限滚动结合React Table v8实现单表格无限数据加载问题求助
解决React Query无限滚动 + React Table v8单表格展示问题
你现在的核心问题是把每个分页的数据集都渲染成了独立的Table组件,所以页面上会出现多个表格。我们只需要把所有分页的数据合并成一个完整的数据集,传给单个Table组件,再搭配滚动触发下一页加载的逻辑就可以解决问题。下面是分步修改方案:
第一步:合并所有分页数据,渲染单个Table
在父组件中,不要遍历pages渲染多个Table,而是把所有分页里的content合并成一个一维数组,然后传给单个Table:
import { useMemo } from 'react'; // 合并所有分页的 shipment 数据 const allShipments = useMemo(() => { return shipments?.pages.flatMap(page => page.content) || []; }, [shipments?.pages]); // 只渲染一个 Table 组件,传入合并后的完整数据 return ( <div> <Table allShipments={allShipments} /> {/* 后续添加无限滚动触发逻辑 */} {shipments.isFetchingNextPage && <div>加载中...</div>} {!shipments.hasNextPage && <div>没有更多数据了</div>} </div> );
第二步:修改Table组件的Props与数据处理
原来的Table组件接收单个分页的group,现在调整为接收合并后的完整数组,同时更新对应的类型和数据解析逻辑:
import { useMemo, useState } from 'react'; import { useTableInstance, getCoreRowModel } from '@tanstack/react-table'; // 更新Props类型,接收合并后的所有数据 type TableProps = { allShipments: Shipment[]; }; // 列定义保持不变,这里省略重复代码 const table = createTable().setRowType<ShipmentRow>(); const defaultColumns = [/* 你的列定义代码 */]; const Table = ({ allShipments }: TableProps) => { // 解析所有数据,而不是单个分页的数据 const parsedShipments = useMemo( () => allShipments.map((shipment: Shipment) => ({ id: shipment.id, orderDate: dayjs(shipment.createDate).format("DD MMMM"), merchantName: shipment.merchant ? shipment.merchant.name : shipment.senderAddress.name, senderTown: shipment.senderAddress.town, receiverTown: shipment.receiverAddress.town, receivingDriver: shipment.receivingDriver ? `${shipment.receivingDriver.name} ${shipment.receivingDriver.surname}` : "Atanmadı", deliveryDriver: shipment.deliveryDriver ? `${shipment.deliveryDriver.name} ${shipment.deliveryDriver.surname}` : "Atanmadı", totalDeciOct: shipment.totalDeciOct, totalPartnerPrice: shipment.totalPartnerPrice, abbreviation: shipment.merchant ? shipment.merchant.abbreviation : shipment.partnerCompany.abbreviation, deliveryAttempts: shipment.deliveryAttempts, rendezvousDeliveryDate: shipment.rendezvousDeliveryDate, status: shipment.status, })) ?? [], [allShipments] ); const [columns] = useState<typeof defaultColumns>(() => [...defaultColumns]); const instance = useTableInstance(table, { data: parsedShipments, columns, getCoreRowModel: getCoreRowModel(), }); // 表格渲染结构保持不变,现在会展示所有合并后的数据 return ( <S.Table> <S.HeadTbody> {instance.getHeaderGroups().map((headerGroup) => ( <tr key={headerGroup.id}> {headerGroup.headers.map((header) => ( <S.HeadTh key={header.id} colSpan={header.colSpan}> {header.isPlaceholder ? null : header.renderHeader()} </S.HeadTh> ))} </tr> ))} </S.HeadTbody> <S.DataTbody> {instance.getRowModel().rows.map((row) => ( <S.StyledTr key={row.id}> {row.getVisibleCells().map((cell) => ( <S.StyledTh key={cell.id}>{cell.renderCell()}</S.StyledTh> ))} </S.StyledTr> ))} </S.DataTbody> </S.Table> ); }; export default Table;
第三步:实现无限滚动触发下一页加载
我们需要监听滚动位置,当用户滚动到页面底部时,触发React Query的fetchNextPage方法。这里提供两种常用实现方式:
方式1:使用原生Intersection Observer API
在父组件中添加一个触发元素,用Intersection Observer监听它是否进入视口:
import { useRef, useEffect } from 'react'; const loadMoreRef = useRef<HTMLDivElement>(null); useEffect(() => { const observer = new IntersectionObserver( (entries) => { const entry = entries[0]; // 当元素进入视口、当前未加载下一页、还有更多数据时,触发加载 if (entry.isIntersecting && !shipments.isFetchingNextPage && shipments.hasNextPage) { shipments.fetchNextPage(); } }, { threshold: 0.1 } ); const currentRef = loadMoreRef.current; if (currentRef) observer.observe(currentRef); return () => { if (currentRef) observer.unobserve(currentRef); }; }, [shipments.hasNextPage, shipments.isFetchingNextPage, shipments.fetchNextPage]); // 在Table下方添加触发元素 return ( <div> <Table allShipments={allShipments} /> <div ref={loadMoreRef} style={{ height: '20px', margin: '20px 0' }}> {shipments.isFetchingNextPage && <span>加载中...</span>} </div> {!shipments.hasNextPage && <div>没有更多数据了</div>} </div> );
方式2:使用第三方库简化开发
如果不想手写监听逻辑,可以用react-infinite-scroll-component库:
首先安装依赖:
npm install react-infinite-scroll-component
然后在父组件中使用:
import InfiniteScroll from 'react-infinite-scroll-component'; return ( <InfiniteScroll dataLength={allShipments.length} next={shipments.fetchNextPage} hasMore={shipments.hasNextPage} loader={<div>加载中...</div>} endMessage={<div>没有更多数据了</div>} > <Table allShipments={allShipments} /> </InfiniteScroll> );
关键注意事项
- 确保你的
useInfiniteQuery配置了正确的getNextPageParam,这样React Query才能正确判断是否还有下一页数据。 - 用
useMemo包裹合并后的数据和解析后的数据,避免不必要的组件重渲染。 - 新数据加载后,React Table会自动更新,因为
parsedShipments依赖allShipments,而allShipments会随着shipments.pages更新。
内容的提问来源于stack exchange,提问作者1sahinomer1
相关产品推荐
相关产品推荐

