无限加载
无限加载是目前UI设计时比较常用的一个特性,在一些情况下可以直接替代传统的分页技术。传统的分页技术在使用SWR实现的时候已经非常简单,只需要确定URL即可。
例如以下专门用于获取分页页面的组件。
function Page({ page }) {
const { data } = useSWR(`/api/data?page=${page}`, fetcher);
return data.map(item => <div key={item.id}>{item.name}</div>);
}
对于普通的页面加载是可以这样来使用的,但是对于无限加载来说,通常都需要通过一个Hook来触发多个请求,这时就需要使用SWR提供的新的Hook了。useSWRInfinite
Hook的使用格式如下。
const { data, error, isValidating, mutate, size, setSize } = useSWRInfinite(getKey, fetcher?, options?);
从useSWRInfinite
的使用格式可以看出来,它与useSWR
的一个不同点在于,useSWRInfinite
需要传入一个函数来返回key
,而不是直接使用一个字符串,现在这个getKey
函数也固定了所接受的参数,第一个是当前页面的索引,第二个是上一页的数据。而且useSWRInfinite
返回的内容也发生了一些变化。主要的变化有以下这些。
data
,虽然与useSWR
返回数据的数据项名称一样,但是useSWRInfinite
返回的是一个数组,其中每个元素都是一个请求的返回值。size
,即将请求和返回的页面数量。setSize
,设置需要请求的页面数量。
useSWRInfinite
基本上可以使用useSWR
所有的配置参数,但还多了以下几个配置项。
initialSize
,设置最初应加载的页面数量,默认为1
。revalidateAll
,设置始终尝试重新验证所有页面,默认为false
。persistSize
,设置当第一页的key发生变化的时候,是否将page size或者initialSize
重置为1
,默认为false
。
以下是一个useSWRInfinite
的使用示例。
const getKey = (pageIndex, previousPage) => {
// 如果getKey函数返回null,那么请求就不会再开始
if (previousPage && !previousPage.length) return null;
// 如果回到了首页,那么就可以传送不带有page参数的key
if (pageIndex == 0) return `/orders`;
return `/orders?page=${pageIndex}`;
};
function Orders() {
const { data, size, setSize } = useSWRInfinite(getKey, fetcher);
if (!data) return <Loading />;
// 计算已经获取的内容数量
let orderCount = 0;
data.forEach(item => (orderCount += item.length));
return (
<div>
<p>{orderCount} Orders listed.</p>
{data.map((orders, index) => {
return orders.map(order => <Order detail={order} />);
})}
<button onClick={() => setSize(size + 1)}>Load More</button>
</div>
);
}
useSWRInfinite
在使用的使用还有一些状态判断技巧,在需要的时候可以参考以下示例。
!data && !error
,是否正在加载第一页数据。(!data && !error) || (size > 0 && data && typeof data[size - 1] === "undefined")
,是否正在加载更多的页面。data?.[0]?.length === 0
,数据服务端没有返回数据。isValidating && data && data.length === size
,是否正在刷新数据。