看名字就能猜出来,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.28at.com/showinfo-26-98195-0.html如何使用 React Query 做下拉数据自动刷新?
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
上一篇: 聊聊大文件分片上传和分片下载