import React from 'react' import ReactDOM from 'react-dom' import { QueryClient, QueryClientProvider, useInfiniteQuery, } from '@tanstack/react-query' import './index.css' import { useVirtualizer } from '@tanstack/react-virtual' const queryClient = new QueryClient() async function fetchServerPage( limit: number, offset: number = 0, ): Promise<{ rows: Array<string>; nextOffset: number }> { const rows = new Array(limit) .fill(0) .map((_, i) => `Async loaded row #${i + offset * limit}`) await new Promise((r) => setTimeout(r, 500)) return { rows, nextOffset: offset + 1 } } function App() { const { status, data, error, isFetching, isFetchingNextPage, fetchNextPage, hasNextPage, } = useInfiniteQuery({ queryKey: ['projects'], queryFn: (ctx) => fetchServerPage(10, ctx.pageParam), getNextPageParam: (lastGroup) => lastGroup.nextOffset, initialPageParam: 0, }) const allRows = data ? data.pages.flatMap((d) => d.rows) : [] const parentRef = React.useRef<HTMLDivElement>(null) const rowVirtualizer = useVirtualizer({ count: hasNextPage ? allRows.length + 1 : allRows.length, getScrollElement: () => parentRef.current, estimateSize: () => 100, overscan: 5, }) React.useEffect(() => { const [lastItem] = [...rowVirtualizer.getVirtualItems()].reverse() if (!lastItem) { return } if ( lastItem.index >= allRows.length - 1 && hasNextPage && !isFetchingNextPage ) { fetchNextPage() } }, [ hasNextPage, fetchNextPage, allRows.length, isFetchingNextPage, rowVirtualizer.getVirtualItems(), ]) return ( <div> <p> This infinite scroll example uses React Query's useInfiniteScroll hook to fetch infinite data from a posts endpoint and then a rowVirtualizer is used along with a loader-row placed at the bottom of the list to trigger the next page to load. </p> <br /> <br /> {status === 'pending' ? ( <p>Loading...</p> ) : status === 'error' ? ( <span>Error: {error.message}</span> ) : ( <div ref={parentRef} className="List" style={{ height: `500px`, width: `100%`, overflow: 'auto', }} > <div style={{ height: `${rowVirtualizer.getTotalSize()}px`, width: '100%', position: 'relative', }} > {rowVirtualizer.getVirtualItems().map((virtualRow) => { const isLoaderRow = virtualRow.index > allRows.length - 1 const post = allRows[virtualRow.index] return ( <div key={virtualRow.index} className={ virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven' } style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: `${virtualRow.size}px`, transform: `translateY(${virtualRow.start}px)`, }} > {isLoaderRow ? hasNextPage ? 'Loading more...' : 'Nothing more to load' : post} </div> ) })} </div> </div> )} <div> {isFetching && !isFetchingNextPage ? 'Background Updating...' : null} </div> <br /> <br /> {process.env.NODE_ENV === 'development' ? ( <p> <strong>Notice:</strong> You are currently running React in development mode. Rendering performance will be slightly degraded until this application is built for production. </p> ) : null} </div> ) } ReactDOM.render( <React.StrictMode> <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider> </React.StrictMode>, document.getElementById('root'), )
import React from 'react' import ReactDOM from 'react-dom' import { QueryClient, QueryClientProvider, useInfiniteQuery, } from '@tanstack/react-query' import './index.css' import { useVirtualizer } from '@tanstack/react-virtual' const queryClient = new QueryClient() async function fetchServerPage( limit: number, offset: number = 0, ): Promise<{ rows: Array<string>; nextOffset: number }> { const rows = new Array(limit) .fill(0) .map((_, i) => `Async loaded row #${i + offset * limit}`) await new Promise((r) => setTimeout(r, 500)) return { rows, nextOffset: offset + 1 } } function App() { const { status, data, error, isFetching, isFetchingNextPage, fetchNextPage, hasNextPage, } = useInfiniteQuery({ queryKey: ['projects'], queryFn: (ctx) => fetchServerPage(10, ctx.pageParam), getNextPageParam: (lastGroup) => lastGroup.nextOffset, initialPageParam: 0, }) const allRows = data ? data.pages.flatMap((d) => d.rows) : [] const parentRef = React.useRef<HTMLDivElement>(null) const rowVirtualizer = useVirtualizer({ count: hasNextPage ? allRows.length + 1 : allRows.length, getScrollElement: () => parentRef.current, estimateSize: () => 100, overscan: 5, }) React.useEffect(() => { const [lastItem] = [...rowVirtualizer.getVirtualItems()].reverse() if (!lastItem) { return } if ( lastItem.index >= allRows.length - 1 && hasNextPage && !isFetchingNextPage ) { fetchNextPage() } }, [ hasNextPage, fetchNextPage, allRows.length, isFetchingNextPage, rowVirtualizer.getVirtualItems(), ]) return ( <div> <p> This infinite scroll example uses React Query's useInfiniteScroll hook to fetch infinite data from a posts endpoint and then a rowVirtualizer is used along with a loader-row placed at the bottom of the list to trigger the next page to load. </p> <br /> <br /> {status === 'pending' ? ( <p>Loading...</p> ) : status === 'error' ? ( <span>Error: {error.message}</span> ) : ( <div ref={parentRef} className="List" style={{ height: `500px`, width: `100%`, overflow: 'auto', }} > <div style={{ height: `${rowVirtualizer.getTotalSize()}px`, width: '100%', position: 'relative', }} > {rowVirtualizer.getVirtualItems().map((virtualRow) => { const isLoaderRow = virtualRow.index > allRows.length - 1 const post = allRows[virtualRow.index] return ( <div key={virtualRow.index} className={ virtualRow.index % 2 ? 'ListItemOdd' : 'ListItemEven' } style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: `${virtualRow.size}px`, transform: `translateY(${virtualRow.start}px)`, }} > {isLoaderRow ? hasNextPage ? 'Loading more...' : 'Nothing more to load' : post} </div> ) })} </div> </div> )} <div> {isFetching && !isFetchingNextPage ? 'Background Updating...' : null} </div> <br /> <br /> {process.env.NODE_ENV === 'development' ? ( <p> <strong>Notice:</strong> You are currently running React in development mode. Rendering performance will be slightly degraded until this application is built for production. </p> ) : null} </div> ) } ReactDOM.render( <React.StrictMode> <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider> </React.StrictMode>, document.getElementById('root'), )
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.