在本文中,我们将探索如何使用 React Query 构建提要页面!
这是我们将要创建的内容:
本文不会涵盖构建应用程序所涉及的每个步骤和细节。
相反,我们将重点关注关键功能,特别是“无限滚动”和“滚动到顶部”功能。
如果您有兴趣咨询整个实现,您可以在此 GitHub 存储库中找到完整的代码库。
首先,我们将使用 Vite 创建 React 应用程序,命令如下:
npm create vite@latest feed-page-rq -- --template react-ts
并且,我们将安装所需的依赖项,axios 和 react-query:
npm install axios @tanstack/react-query@4
我们还需要模拟 RESTful 服务器,因此我们将使用 json-server,它允许我们通过为 React 应用程序提供虚假 API 端点来模拟后端。
我们将与包含以下属性的帖子实体合作:
{ "id": "1", "title": "Sample Post Title", "body": "This is a sample post body", "userId": "2", "createdAt": 1728334799169 // date timestamp }
服务器设置完毕后,我们将使用以下命令运行它:
npx json-server --watch db.json
“无限滚动”功能的机制很简单:
当用户滚动帖子列表并接近容器底部时,React Query 将查找下一批帖子。重复此过程,直到没有更多帖子可加载。
我们通过将当前滚动位置(scrollTop)添加到可见屏幕高度(clientHeight)并将此总和与进行比较来验证用户是否接近底部容器的总高度 (scrollHeight)。
如果总和大于或等于容器总高度,我们要求 React Query 获取下一页。
const { scrollTop, scrollHeight, clientHeight } = elemRef.current; if(scrollTop clientHeight >= scrollHeight) { fetchNextPage(); }
首先,我们将创建一个自定义钩子来包装 React Query 的 useInfiniteQuery。
在自定义挂钩中,我们配置查询以逐页获取帖子,指定初始页码和检索下一页的函数:
import { QueryFunctionContext, useInfiniteQuery } from "@tanstack/react-query"; import axios from "axios"; const URL = "http://localhost:3000"; const POSTS = "posts"; export const fetchInfinitePosts = async ({ pageParam, }: QueryFunctionContext) => { const result = await axios.get( `${URL}/${POSTS}?_sort=-createdAt&_page=${pageParam}&_per_page=10`, ); return result.data; }; export const useInfinitePosts = () => { return useInfiniteQuery({ queryKey: [POSTS], queryFn: fetchInfinitePosts, initialPageParam: 1, getNextPageParam: (lastPage) => lastPage.next, }); };
现在,我们将在组件中使用自定义挂钩来显示帖子列表。
为此,我们首先循环访问页面,然后迭代每个页面中的帖子以呈现它们。
import { useInfinitePosts } from './hooks/useInfinitePosts'; const PostList = () => { const { data: postLists } = useInfinitePosts(); return ({postLists?.pages.map((page) => page.data.map(post => (); }; export default PostList;)) )}{post.title}
{post.body}
要实现无限滚动行为,我们需要向显示帖子的容器添加滚动事件侦听器。此事件侦听器会触发 onScroll 函数,该函数会检查用户是否位于容器底部附近,如果是,则调用 fetchNextPage 来加载更多内容。
import React, { useRef, useEffect } from 'react'; import { useInfinitePosts } from './hooks/useInfinitePosts'; const PostList = () => { const { data: postLists, fetchNextPage } = useInfinitePosts(); const elemRef = useRef(null); const onScroll = useCallback(() => { if (elemRef.current) { const { scrollTop, scrollHeight, clientHeight } = elemRef.current; const isNearBottom = scrollTop clientHeight >= scrollHeight; if(isNearBottom) { fetchNextPage(); } } }, [fetchNextPage]); useEffect(() => { const innerElement = elemRef.current; if (innerElement) { innerElement.addEventListener("scroll", onScroll); return () => { innerElement.removeEventListener("scroll", onScroll); }; } }, [onScroll]); return ({postLists?.pages.map((page, i) => page.data.map(post => (); }; export default PostList;)) )}{post.title}
{post.body}
接下来,我们将创建一个“滚动到顶部”按钮,该按钮在添加新帖子时出现。此按钮可以让用户快速返回顶部查看最新更新。
由于帖子按创建日期排序,因此任何新添加的帖子都会显示在列表顶部。
我们的功能逻辑将建立在这个前提之上。
我们首先创建一个新查询来获取并缓存最新创建的帖子。我们将这篇文章称为 prevNewestPost。
我们希望 prevNewestPost 落后几步,或者最多与列表中的第一个帖子匹配。因此,我们将手动控制其重新获取。
我们将通过在查询选项中设置enabled: false来实现这一点。
export const fetchNewestPost = async () => { const result = await axios.get(`${URL}/${POSTS}?_sort=-createdAt`); return result.data[0]; }; export const useNewestPost = () => { return useQuery({ queryKey: [POSTS, "newest"], queryFn: () => fetchNewestPost(), enabled: false, }); };
使用 React Query,帖子列表会根据特定事件自动更新。 (这里是这些事件的完整列表的文档链接。)
我们将使用此更新的列表通过比较 prevNewestPost 与第一篇文章来确定何时显示“滚动到顶部”按钮。
如果它们不同,则表明已添加新帖子,因此将显示“滚动到顶部”按钮。
setIsShowButton(postLists?.pages[0].data[0].id !== prevNewestPost?.id);
当用户位于帖子列表容器的顶部时,我们不应该显示“滚动到顶部”按钮。
因此,当用户到达顶部时,我们需要通过触发查询重新获取来将 prevNewestPost 与当前最新帖子重新同步。
const { data: prevNewestPost, refetch } = useNewestPost(); const [isShowButton, setIsShowButton] = useState(false); useEffect(() => { if (!isNearTop) { setIsShowButton(postLists?.pages[0].data[0].id !== prevNewestPost?.id); } else { setIsShowButton(false); refetch(); } }, [postLists, prevNewestPost, isNearTop]);
点击ToTopBtn按钮将滚动到列表顶部,触发现有逻辑隐藏按钮并重新获取数据以将prevNewestPost与列表的第一个帖子同步。
import { RefObject } from "react"; type ToTopBtnProps = { elemRef: RefObject; }; export default function ToTopBtn({ elemRef }: ToTopBtnProps) { return ( ); }
为了测试我们的“滚动到顶部”按钮功能,我们需要向提要添加新帖子。
为此,我们将使用 React Query 中的 useMutation 将新帖子添加到服务器,并在每次更改后重新验证缓存的 postList。
我们将设置一个突变,允许我们在用户单击按钮时使用随机数据创建新帖子。
export const savePost = async (post: NewPostData) => axios.post(`${URL}/${POSTS}`, post); export const useAddPost = () => { const queryClient = useQueryClient(); return useMutation({ mutationFn: savePost, onSuccess: () => { queryClient.invalidateQueries({ queryKey: [POSTS] }); }, }); };
export default function AddNewPostBtn() { const mutation = useAddPost(); return ();
在本教程中,我们通过真实用例探索了 React Query 的强大功能,强调了它帮助开发人员构建增强用户体验的动态界面的能力。
免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。
Copyright© 2022 湘ICP备2022001581号-3