」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 使用 React Query 建立 Feed 頁面

使用 React Query 建立 Feed 頁面

發佈於2024-11-08
瀏覽:486

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]刪除
最新教學 更多>
  • 為我的 D&D 表建立自訂 Stream Deck:使用自訂 SDK 解決遊戲手把整合問題
    為我的 D&D 表建立自訂 Stream Deck:使用自訂 SDK 解決遊戲手把整合問題
    作為一個熱情的龍與地下城玩家和技術愛好者,我決定通過創建一個帶有嵌入式屏幕的定制咖啡桌來提升我的遊戲設置。這張桌子顯示地圖、代幣、播放音效,甚至為玩家展示藝術品。所有這一切都透過一個名為 Foundry VTT 的強大平台運行,該平台與 Roll20 類似,但具有更好的定價模型和廣泛的開放模組系統。...
    程式設計 發佈於2024-11-08
  • 如何在 PHP 中有效率地計算兩個日期之間的月份數?
    如何在 PHP 中有效率地計算兩個日期之間的月份數?
    有效找出日期之間的月份計數一個常見的程式設計挑戰是確定兩個日期之間的月份數。在 PHP 中,有許多方法可以解決這個問題。 使用 DateTime 類別 (PHP >= 5.3):PHP 5.3 中引入的 DateTime 類別提供了方便的方法用於日期操作。計算月份差異:$d1 = new DateT...
    程式設計 發佈於2024-11-08
  • Bootstrap:建立和自訂導覽列
    Bootstrap:建立和自訂導覽列
    介紹 Bootstrap 是一個開源框架,廣泛用於 Web 開發,用於建立響應式且適合行動裝置的網站。 Bootstrap 的關鍵元件之一是導覽欄,它是一個水平導覽欄,用於組織和導覽網站的內容。在本文中,我們將討論使用 Bootstrap 建立和自訂導覽列的優點和缺點及其功能。 ...
    程式設計 發佈於2024-11-08
  • 將 WebSocket 與 Python 結合使用
    將 WebSocket 與 Python 結合使用
    什麼是 WebSocket? WebSocket 是一種支援瀏覽器和伺服器之間即時、雙向通訊的協定。傳統的 HTTP 通訊涉及客戶端發送請求和伺服器回應以交換資料。相較之下,使用 WebSocket,一旦建立了初始連接,客戶端和伺服器都可以相互發送和接收訊息,而無需重複建立新連接。...
    程式設計 發佈於2024-11-08
  • 如何在 PHP 中從子網域中提取網域?
    如何在 PHP 中從子網域中提取網域?
    在PHP 中從子域中提取域名在當代Web 開發中,必須解析和檢索域名,甚至是從子域中解析和檢索網域名稱。一個簡單的範例可能包括諸如“here.example.com”或“example.org”之類的網域。為了滿足這一需求,我們提出了一個全面的 PHP 函數,旨在從任何給定的輸入中提取根域名。 結合...
    程式設計 發佈於2024-11-08
  • 如何在多執行緒程式設計中連接向量以獲得最佳效率?
    如何在多執行緒程式設計中連接向量以獲得最佳效率?
    連結向量:深入分析在多執行緒程式設計中,合併結果是一個常見的挑戰。這通常涉及將多個向量組合成單一綜合向量。讓我們探索連接向量以獲得最大效率的最佳方法。 最佳連接方法為了高效的向量連接,最佳實踐是利用保留和插入方法:AB.reserve(A.size() B.size()); // Preallo...
    程式設計 發佈於2024-11-08
  • 如何優化FastAPI以實現高效的JSON資料回傳?
    如何優化FastAPI以實現高效的JSON資料回傳?
    FastAPI 傳回大型 JSON 資料的最佳化透過 FastAPI 傳回大量 JSON 資料集可能是一項耗時的任務。為了解決這個瓶頸,我們探索提高效能的替代方法。 識別瓶頸:使用 json.dumps 將 Parquet 檔案解析為 JSON 的初始方法( ) 和 json.loads() 效率低...
    程式設計 發佈於2024-11-08
  • React:狀態 X 派生狀態
    React:狀態 X 派生狀態
    什麼是派生狀態?考慮文字的一種狀態,然後考慮大寫文字的另一種狀態。 匯出狀態 function Foo() { const [text, setText] = useState('hello, za warudo!'); const [uppercaseText, ...
    程式設計 發佈於2024-11-08
  • 如何使用自訂使用者類型將 PostgreSQL JSON 欄位對應到 Hibernate 實體?
    如何使用自訂使用者類型將 PostgreSQL JSON 欄位對應到 Hibernate 實體?
    將 PostgreSQL JSON 欄位對應到 Hibernate 實體使用 PostgreSQL 資料庫時,常常會遇到以 JSON 格式儲存資料的資料列。為了使用 Hibernate 有效地將這些欄位對應到 Java 實體,選擇適當的資料類型至關重要。 在這種情況下,目前的問題圍繞著將 Postg...
    程式設計 發佈於2024-11-08
  • 確保整個團隊的 Node.js 版本一致
    確保整個團隊的 Node.js 版本一致
    .nvmrc 和 package.json 綜合指南 在現今動態的開發環境中,跨不同專案管理多個 Node.js 版本通常是一項複雜且容易出錯的任務。 Node.js 版本不一致可能會導致許多問題,從意外行為到應用程式完全失敗。 利用 .nvmrc 檔案進行版本控制 在專案中保持一...
    程式設計 發佈於2024-11-08
  • 何時在 JavaScript Promise 中使用 Promise.reject 與 Throw?
    何時在 JavaScript Promise 中使用 Promise.reject 與 Throw?
    JavaScript Promise:Reject 與Throw 之謎使用JavaScript Promise 時,開發人員經常面臨一個困境:他們是否應該使用Promise . reject 或者只是拋出一個錯誤?雖然這兩種方法具有相似的目的,但關於它們的差異和潛在優勢仍然存在混淆。 探索相似之處最...
    程式設計 發佈於2024-11-08
  • 建立 Chrome 擴充功能:快速概述
    建立 Chrome 擴充功能:快速概述
    模组——修改? 如果您喜欢游戏,您就会知道没有什么比玩模组游戏更好的了。这是您最喜欢的游戏,但具有额外的功能、功能和乐趣。现在,想象一下为您的网络浏览体验带来同样的兴奋。这正是浏览器扩展的作用——它们就像浏览器的模组,以您从未想过的方式增强浏览器的功能。 通过 Chrome 扩展程序,您可以调整浏览...
    程式設計 發佈於2024-11-08
  • 如何使用 CSS 設定表格列寬?
    如何使用 CSS 設定表格列寬?
    設定表格列寬表格通常用於呈現表格數據,但調整列寬對於確保可讀性和正確性至關重要結盟。在本文中,我們將探討如何使用 CSS 設定表格列的寬度。 使用 CSS 寬度屬性的方法表格列的寬度可以使用 col 元素的 width 屬性進行設定。寬度值可以以像素為單位指定(例如 width: 200px;),也...
    程式設計 發佈於2024-11-08
  • 如何從 Python 中的巢狀函數存取非局部變數?
    如何從 Python 中的巢狀函數存取非局部變數?
    存取嵌套函數作用域中的非局部變數在Python 中,嵌套函數作用域提供對封閉作用域的訪問。但是,嘗試修改巢狀函數內封閉範圍內的變數可能會導致 UnboundLocalError。 要解決此問題,您有多種選擇:1。使用 'nonlocal' 關鍵字 (Python 3 ):對於 Pyt...
    程式設計 發佈於2024-11-08
  • 使用 CSS 將漸層應用於文字。
    使用 CSS 將漸層應用於文字。
    文字漸變 現在你可以在很多地方看到像文字漸變這樣的好技巧......但是呢?你有沒有想過它們是如何製作的?今天就讓我來教你。 .text-gradient { background: linear-gradient(-25deg, #5662f6 0%, #7fffd4 10...
    程式設計 發佈於2024-11-08

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3