看名字就能猜出來,useInfiniteQuery() 是專門用來應付無限查詢場景的。不僅如此,useInfiniteQuery() API 能力也是基于 useQuery() 的。
之前的文章中我們介紹了 useQuery() 的核心 API,為了找回印象,我們在此貼出來:
import { useQuery } from 'react-query'const { data, error, isError, isFetching, isLoading, isRefetching, isSuccess, refetch,} = useQuery(queryKey, queryFn?, { enabled, onError, onSuccess, refetchOnWindowFocus, retry, staleTime,})
如果我們把這些 API 簡化如下:
const { ...result,} = useQuery(queryKey, queryFn?, { ...options,})
useInfiniteQuery() 其實就是在 useQuery() 基礎之上增添一些無限查詢場景的參數:
const { fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage, isFetchingNextPage, isFetchingPreviousPage, ...result} = useInfiniteQuery(queryKey, ({ pageParam = 1 }) => fetchPage(pageParam), { ...options, getNextPageParam: (lastPage, allPages) => lastPage.nextCursor, getPreviousPageParam: (firstPage, allPages) => firstPage.prevCursor,})
如你所見,增加的 API 其實就是跟上一頁/下一頁查詢動作相關的參數,相比較于自己組裝 的分頁查詢能力的 useQuery(),useInfiniteQuery() 需要配置上一頁/下一頁的參數獲取函數,并提供了相應的查詢調用能力,更加自動化和便捷。
當然,增加的不只是參數,還有 2 處:
一個是 queryFn 參數的入參,多了一個名為 pageParam 的參數。
pageParam 表示當前頁數。這個值是每次 useInfiniteQuery() 調用時,通過 getNextPageParam()/getPreviousPageParam() 返回值自動獲取并傳入 queryFn 的。
第二個還有返回值的數據結構,即 data。
const { data } = useInfiniteQuery()
原來 data 就是表示內部請求方法的返回值。而 useInfiniteQuery() 的返回 data 因為要包含多頁數據(展示舊數據時,還要持有舊數據),因此 data 變更為:
data: { pages: TData[], pageParams: unknown[] }
pages 很好理解,就是用來承載過程中請求到的多頁數據;pageParams 則是每個頁面當時在做數據獲取時使用的查詢參數。
當然語言上說得再多,也是蒼白無力的,實踐出真知。這里我們就舉一個簡單的例子說明 useInfiniteQuery() 的使用。
首先,我們先創建一個獲取數據的請求函數(使用 Fetch API)。
const getPosts = async (pageParam) => { return fetch(`https://jsonplaceholder.typicode.com/posts?_page=${pageParam.page}&_limit=${pageParam.size}`).then(res => res.json())}
接著,使用 useInfiniteQuery() 請求數據:
function Example() { const { isLoading, isError, error, data, } = useInfiniteQuery( 'posts', ({ pageParam }) => getPosts(pageParam), { getNextPageParam: (lastPage, allPages) => ({ page: allPages.length + 1, size: 6 }), refetchOnWindowFocus: false, // Prevent refetching on window focus } ) // ...}
增加下加載中或出現異常時的處理邏輯。
function Example() { // ... if (isLoading) { return <div>Loading...</div>; } if (isError) { return <div>Error: {error.message}</div> } // ...}
最后渲染分頁數據。
function Example() { // ... return ( <div> <ol> {/* (1) */} {data.pages.map((page) => ( {page.map((post) => ( <li key={post.id}>{post.title}</li> ))} ))} </ol> {/* (2) */} <button onClick={() => fetchNextPage()}>More</button> </div> )}
瀏覽器訪問,不幸運是,報錯了。
圖片
完美。
最后,再把完整代碼貼出來,方便大家學習。
import { useEffect, useRef } from 'react'import { QueryClient, QueryClientProvider, useInfiniteQuery } from 'react-query'// Create a clientconst queryClient = new QueryClient()export default function App() { return ( // Provide the client to your App <QueryClientProvider client={queryClient}> <Example /> </QueryClientProvider> )}const getPosts = async (pageParam = { page: 1, size: 25 }) => { return fetch(`https://jsonplaceholder.typicode.com/posts?_page=${pageParam.page}&_limit=${pageParam.size}`).then(res => { const total = res.headers.get('X-Total-Count') return res.json().then(data => { return { total, data, hasMore: pageParam.page * pageParam.size < total } }) })}function Example() { const { isLoading, isFetchingNextPage, hasNextPage, isError, error, data, fetchNextPage } = useInfiniteQuery( 'posts', ({ pageParam }) => getPosts(pageParam), { getNextPageParam: (lastPage, allPages) => { return lastPage.hasMore ? { page: allPages.length + 1, size: 25 } : undefined }, refetchOnWindowFocus: false, // Prevent refetching on window focus } ) const loadMoreRef = useRef(null); useEffect(() => { const observer = new IntersectionObserver((entries) => { if (entries[0].isIntersecting && hasNextPage) { fetchNextPage(); } }); if (loadMoreRef.current) { observer.observe(loadMoreRef.current); } return () => observer.disconnect(); }, [hasNextPage, fetchNextPage]); if (isLoading) { return <div>Loading...</div>; } if (isError) { return <div>Error: {error.message}</div> } return ( <div> <p>總共 <strong>{data.pages[0].total}</strong> 條數據</p> <ol> {data.pages.map((page) => ( <> {page.data.map((post) => ( <li key={post.id}>{post.title}</li> ))} </> ))} </ol> <div className="loadMore" style={{ height: '30px', lineHeight: '30px' }} ref={loadMoreRef}> { isFetchingNextPage ? <span>Loading...</span> : <span>--- 我是有底線的 ---</span> } </div> </div> )}
本文我們講述了 React Query 中用于無限查詢 API useInfiniteQuery() 的使用。
通過循序漸進的 3 個案例,最終實現了一個下拉到底后自動新數據的交互效果,還是比較好實現的。
當然,本文只是以“下一頁”舉例,“上一頁”與此同理。
希望本位講述的內容能夠對你的工作有所幫助。感謝閱讀,再見。
[1]React Query 是做什么的?: https://juejin.cn/post/7378015213348257855
[2]一個數據獲竟然被 React Query 玩出這么多花樣來!: https://juejin.cn/post/7380342160581918731
[3]React Query 的 useQuery 竟也內置了分頁查詢支持!: https://juejin.cn/post/7380569775686746151
[4]IntersectionObserver API: https://ruanyifeng.com/blog/2016/11/intersectionobserver_api.html
本文鏈接:http://www.www897cc.com/showinfo-26-98195-0.html如何使用 React Query 做下拉數據自動刷新?
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 聊聊大文件分片上傳和分片下載