”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 React Query 构建 Feed 页面

使用 React Query 构建 Feed 页面

发布于2024-11-09
浏览:110

Building a Feed Page Using React Query

目标

在本文中,我们将探索如何使用 React Query 构建提要页面!

这是我们将要创建的内容:

本文不会涵盖构建应用程序所涉及的每个步骤和细节。
相反,我们将重点关注关键功能,特别是“无限滚动”和“滚动到顶部”功能。
如果您有兴趣咨询整个实现,您可以在此 GitHub 存储库中找到完整的代码库。

设置应用程序

首先,我们将使用 Vite 创建 React 应用程序,命令如下:

npm create vite@latest feed-page-rq -- --template react-ts

并且,我们将安装所需的依赖项,axiosreact-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();
  }

第 1 步:配置 useInfiniteQuery

首先,我们将创建一个自定义钩子来包装 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,
  });
};

第 2 步:在 PostList 中显示帖子

现在,我们将在组件中使用自定义挂钩来显示帖子列表。
为此,我们首先循环访问页面,然后迭代每个页面中的帖子以呈现它们。

import { useInfinitePosts } from './hooks/useInfinitePosts';

const PostList = () => {
  const { data: postLists } = useInfinitePosts();

  return (
    
{postLists?.pages.map((page) => page.data.map(post => (

{post.title}

{post.body}

)) )}
); }; export default PostList;

第 3 步:实现无限滚动行为

要实现无限滚动行为,我们需要向显示帖子的容器添加滚动事件侦听器。此事件侦听器会触发 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 => (

{post.title}

{post.body}

)) )}
); }; export default PostList;

实现“滚动到顶部”

接下来,我们将创建一个“滚动到顶部”按钮,该按钮在添加新帖子时出现。此按钮可以让用户快速返回顶部查看最新更新。
由于帖子按创建日期排序,因此任何新添加的帖子都会显示在列表顶部。
我们的功能逻辑将建立在这个前提之上。

第 1 步:为 prevNewestPost 创建查询

我们首先创建一个新查询来获取并缓存最新创建的帖子。我们将这篇文章称为 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,
  });
};

第 2 步:将上一篇最新帖子与第一篇帖子进行比较

使用 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]);

第 4 步:创建滚动到顶部按钮

点击ToTopBtn按钮将滚动到列表顶部,触发现有逻辑隐藏按钮并重新获取数据以将prevNewestPost与列表的第一个帖子同步。

import { RefObject } from "react";

type ToTopBtnProps = {
  elemRef: RefObject;
};

export default function ToTopBtn({ elemRef }: ToTopBtnProps) {
  return (
    
         
  ); }

第 5 步:通过添加新帖子进行测试

为了测试我们的“滚动到顶部”按钮功能,我们需要向提要添加新帖子。
为此,我们将使用 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 的强大功能,强调了它帮助开发人员构建增强用户体验的动态界面的能力。

版本声明 本文转载于:https://dev.to/mohamed_hammi/build-a-feed-page-using-react-query-1nbi?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 将图片浮动到底部右侧并环绕文字的技巧
    将图片浮动到底部右侧并环绕文字的技巧
    在Web设计中围绕在Web设计中,有时可以将图像浮动到页面右下角,从而使文本围绕它缠绕。这可以在有效地展示图像的同时创建一个吸引人的视觉效果。 css位置在右下角,使用css float and clear properties: img { 浮点:对; ...
    编程 发布于2025-07-24
  • 在GO中构造SQL查询时,如何安全地加入文本和值?
    在GO中构造SQL查询时,如何安全地加入文本和值?
    在go中构造文本sql查询时,在go sql queries 中,在使用conting and contement和contement consem per时,尤其是在使用integer per当per当per时,per per per当per. 在GO中实现这一目标的惯用方法是使用fmt.spr...
    编程 发布于2025-07-24
  • 同实例无需转储复制MySQL数据库方法
    同实例无需转储复制MySQL数据库方法
    在同一实例上复制一个MySQL数据库而无需转储在同一mySQL实例上复制数据库,而无需创建InterMediate sqql script。以下方法为传统的转储和IMPORT过程提供了更简单的替代方法。 直接管道数据 MySQL手动概述了一种允许将mysqldump直接输出到MySQL clie...
    编程 发布于2025-07-24
  • Java的Map.Entry和SimpleEntry如何简化键值对管理?
    Java的Map.Entry和SimpleEntry如何简化键值对管理?
    A Comprehensive Collection for Value Pairs: Introducing Java's Map.Entry and SimpleEntryIn Java, when defining a collection where each element com...
    编程 发布于2025-07-24
  • 如何在Chrome中居中选择框文本?
    如何在Chrome中居中选择框文本?
    选择框的文本对齐:局部chrome-inly-ly-ly-lyly solument 您可能希望将文本中心集中在选择框中,以获取优化的原因或提高可访问性。但是,在CSS中的选择元素中手动添加一个文本 - 对属性可能无法正常工作。初始尝试 state)</option> < op...
    编程 发布于2025-07-24
  • 如何同步迭代并从PHP中的两个等级阵列打印值?
    如何同步迭代并从PHP中的两个等级阵列打印值?
    同步的迭代和打印值来自相同大小的两个数组使用两个数组相等大小的selectbox时,一个包含country代码的数组,另一个包含乡村代码,另一个包含其相应名称的数组,可能会因不当提供了exply for for for the uncore for the forsion for for ytry...
    编程 发布于2025-07-24
  • 为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
    为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
    网格超过身体,用100%grid-template-columns 为什么在grid-template-colms中具有100%的显示器,当位置设置为设置的位置时,grid-template-colly修复了?问题: 考虑以下CSS和html: class =“ snippet-code”> g...
    编程 发布于2025-07-24
  • 在C#中如何高效重复字符串字符用于缩进?
    在C#中如何高效重复字符串字符用于缩进?
    在基于项目的深度下固定字符串时,重复一个字符串以进行凹痕,很方便有效地有一种有效的方法来返回字符串重复指定的次数的字符串。使用指定的次数。 constructor 这将返回字符串“ -----”。 字符串凹痕= new String(' - ',depth); console.Wr...
    编程 发布于2025-07-24
  • 为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    为什么Microsoft Visual C ++无法正确实现两台模板的实例?
    The Mystery of "Broken" Two-Phase Template Instantiation in Microsoft Visual C Problem Statement:Users commonly express concerns that Micro...
    编程 发布于2025-07-24
  • 如何解决AppEngine中“无法猜测文件类型,使用application/octet-stream...”错误?
    如何解决AppEngine中“无法猜测文件类型,使用application/octet-stream...”错误?
    appEngine静态文件mime type override ,静态文件处理程序有时可以覆盖正确的mime类型,在错误消息中导致错误消息:“无法猜测mimeType for for file for file for [File]。 application/application/octet...
    编程 发布于2025-07-24
  • 编译器报错“usr/bin/ld: cannot find -l”解决方法
    编译器报错“usr/bin/ld: cannot find -l”解决方法
    错误:“ usr/bin/ld:找不到-l “ 此错误表明链接器在链接您的可执行文件时无法找到指定的库。为了解决此问题,我们将深入研究如何指定库路径并将链接引导到正确位置的详细信息。添加库搜索路径的一个可能的原因是,此错误是您的makefile中缺少库搜索路径。要解决它,您可以在链接器命令中添加...
    编程 发布于2025-07-24
  • 如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    postgresql:为每个唯一标识符在postgresql中提取最后一行,您可能需要遇到与数据集合中每个不同标识的信息相关的信息。考虑以下数据:[ 1 2014-02-01 kjkj 在数据集中的每个唯一ID中检索最后一行的信息,您可以在操作员上使用Postgres的有效效率: id dat...
    编程 发布于2025-07-24
  • 在Ubuntu/linux上安装mysql-python时,如何修复\“ mysql_config \”错误?
    在Ubuntu/linux上安装mysql-python时,如何修复\“ mysql_config \”错误?
    mysql-python安装错误:“ mysql_config找不到”“ 由于缺少MySQL开发库而出现此错误。解决此问题,建议在Ubuntu上使用该分发的存储库。使用以下命令安装Python-MysqldB: sudo apt-get安装python-mysqldb sudo pip in...
    编程 发布于2025-07-24
  • 如何避免Go语言切片时的内存泄漏?
    如何避免Go语言切片时的内存泄漏?
    ,a [j:] ...虽然通常有效,但如果使用指针,可能会导致内存泄漏。这是因为原始的备份阵列保持完整,这意味着新切片外部指针引用的任何对象仍然可能占据内存。 copy(a [i:] 对于k,n:= len(a)-j i,len(a); k
    编程 发布于2025-07-24
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,以便更快地搜索这些前缀。理解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-07-24

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3