使用useInfiniteQuery和封装的useInfiniteScrollHooks做一个无限feed流

使用useInfiniteQuery和封装的useInfiniteScrollHooks做一个无限feed流

在这篇文章中,我们将使用 React Query 的 useInfiniteQuery hook 和一个自定义的 useInfiniteScroll hook 来创建一个无限滚动的 feed 流。

使用 useInfiniteQuery 获取数据

首先,我们使用 useInfiniteQuery 来获取 feed 数据。这个 hook 接受一个查询键和一个获取数据的异步函数作为参数,并返回一个对象,该对象包含了我们需要的所有状态和函数。

在这个例子中,我们使用 getFeed 函数来获取数据,该函数接受一个页码作为参数。我们还提供了一个 getNextPageParam 函数来确定下一页的参数。

tsx 复制代码
const {
  data: FeedData = { pages: [], pageParams: [] },
  isLoading: FeedIsLoading,
  isError: FeedIsError,
  fetchNextPage,
  isFetchingNextPage,
  hasNextPage,
} = useInfiniteQuery(['getFeed'], ({ pageParam = 0 }) => getFeed(pageParam), {
  getNextPageParam: (lastPage, pages) => {
    if (lastPage.length === 0) return undefined;
    return pages.length + 1;
  },
});

当前后端给我们的接口是这样的,我们将它封装成getFeed

ts 复制代码
// 获取feed
export async function getFeed(pageParam: number, per_page: number = 5) {
  return axios.get({
    url: 'feed',
    params: {
      page: pageParam, // 第几页
      per_page,        // 一次获取多少页
    },
  });
}

使用自定义的 useInfiniteScroll Hook

然后,我们使用自定义的 useInfiniteScroll hook 来实现无限滚动。这个 hook 接受当前是否正在获取下一页、是否有下一页和获取下一页的函数作为参数,并返回一个可以被赋予给元素的 ref。

当这个 ref 被赋予给的元素出现在视口中时,就会调用获取下一页的函数。

tsx 复制代码
const lastElementRef = useInfiniteScroll(
  isFetchingNextPage,
  hasNextPage,
  fetchNextPage
);

再解释一下这个hooks,这个 Hook 接受三个参数:

  1. isFetching:一个布尔值,表示是否正在获取下一页的数据。
  2. hasNextPage:一个布尔值,表示是否还有下一页的数据。
  3. fetchNextPage:一个函数,当需要获取下一页的数据时会被调用。

这个 Hook会 返回一个函数,这个函数接受一个 HTMLDivElement 也就是一个ref对象并对其进行操作。当这个元素出现在视口中时,就会调用 fetchNextPage 函数来获取下一页的数据。

它 的工作原理是使用 IntersectionObserver API 来观察一个元素(在这个例子中是传入的 node)。当这个元素出现在视口中时,IntersectionObserver 会调用它的回调函数。在这个回调函数中,我们检查是否正在获取数据(通过 isFetching)和是否还有下一页(通过 hasNextPage)。如果不在获取数据并且还有下一页,就调用 fetchNextPage 函数。

最重要的就是这个IntersectionObserver的方法

MDN地址:developer.mozilla.org/en-US/docs/...

tsx 复制代码
import { RefObject, useRef, useCallback } from 'react';
​
type FetchNextPageFunction = () => void;
​
export function useInfiniteScroll(
  isFetching: boolean,
  hasNextPage: boolean | undefined,
  fetchNextPage: FetchNextPageFunction
): (node: HTMLDivElement) => void {
  const observer = useRef<IntersectionObserver | null>(null);
  const lastElementRef = useCallback(
    (node: HTMLDivElement) => {
      if (isFetching) return;
      if (observer.current) observer.current.disconnect();
      observer.current = new IntersectionObserver((entries) => {
        if (entries[0].isIntersecting && hasNextPage) {
          fetchNextPage();
        }
      });
      if (node) observer.current.observe(node);
    },
    [isFetching, hasNextPage]
  );
​
  return lastElementRef;
}
​

渲染 Feed

最后,我们渲染 feed。对于每一页,我们都渲染一个 div,并在其中渲染每个 feed item。我们使用 Card 组件来渲染每个 item。

当有下一页时,我们在列表底部添加一个 "Loading..." 的 div,并将其 ref 设置为 lastElementRef。当这个 div 出现在视口中时,就会触发获取下一页的函数。

在使用FeedData要注意它的是一个{ pages: [], pageParams: [] }对象,并且pages是一个二维数组,它是全部的数据分组的集合。每次fetchNextPage就会使group+1,这个新的group的数组就是获取到的下一页数组

tsx 复制代码
return (
  <>
    {FeedData.pages &&
      FeedData.pages.map((group, i) => ( 
        <div key={i}>
          {group.map((feedItem: any) => (
            <Card key={feedItem.uid} item={feedItem} /> //Card 是自己的组件
          ))}
        </div>
      ))}
    {hasNextPage && (
      <div ref={lastElementRef} className=' text-center p-4'>
        Loading...
      </div>
    )}
  </>
);

通过这种方式,我们就可以创建一个无限滚动的 feed 流了。

相关推荐
1024肥宅7 分钟前
JavaScript 拷贝全解析:从浅拷贝到深拷贝的完整指南
前端·javascript·ecmascript 6
欧阳天风18 分钟前
js实现鼠标横向滚动
开发语言·前端·javascript
局i1 小时前
Vue 指令详解:v-for、v-if、v-show 与 {{}} 的妙用
前端·javascript·vue.js
码界奇点1 小时前
Java Web学习 第15篇jQuery从入门到精通的万字深度解析
java·前端·学习·jquery
小鑫同学2 小时前
Alias Assistant:新一代 macOS Shell 别名管理解决方案
前端·前端工程化
꒰ঌ小武໒꒱2 小时前
RuoYi-Vue 前端环境搭建与部署完整教程
前端·javascript·vue.js·nginx
名字越长技术越强2 小时前
前端之相对路径
前端
望道同学3 小时前
PMP/信息系统项目管理师 9 张 思维导图【考试必备】
前端·后端·程序员
局i3 小时前
Vue 中 v-text 与 v-html 的区别:文本渲染与 HTML 解析的抉择
前端·javascript·vue.js
菜鸟冲锋号4 小时前
问题:增量关联(实时同步新数据) 这个场景中,如果hudi_pay 变更了一条数据,hudi_order_pay_join 结果的数据会跟着变化吗
服务器·前端·数据库