この記事では、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 と最初の投稿を比較することにより、「Scroll To Top」ボタンをいつ表示するかを決定します。
異なる場合は、新しい投稿が追加されたことを示しているため、[トップへスクロール] ボタンが表示されます。
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 の力を探り、開発者がユーザー エクスペリエンスを向上させる動的なインターフェイスを構築するのに役立つ React Query の機能に焦点を当てました。
免責事項: 提供されるすべてのリソースの一部はインターネットからのものです。お客様の著作権またはその他の権利および利益の侵害がある場合は、詳細な理由を説明し、著作権または権利および利益の証拠を提出して、電子メール [email protected] に送信してください。 できるだけ早く対応させていただきます。
Copyright© 2022 湘ICP备2022001581号-3