TanStack Queryを使った場合
TanStack Queryを使うと、データの取得、キャッシング、同期が簡単になります。以下は、APIからデータを取得する基本的な例です。
import React from 'react';
import { useQuery } from '@tanstack/react-query';
const fetchTodos = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/todos');
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
};
const TodoList = () => {
const { data, error, isLoading } = useQuery(['todos'], fetchTodos);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{data.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
};
export default TodoList;
TanStack Queryを使わない場合
TanStack Queryを使わない場合、状態管理やデータの取得を自前で行う必要があります。以下は、同様の機能を実装した例です。
import React, { useEffect, useState } from 'react';
const TodoList = () => {
const [todos, setTodos] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchTodos = async () => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
setTodos(data);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
fetchTodos();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
);
};
export default TodoList;
比較
-
TanStack Queryを使った場合:
- 自動的なキャッシングやリフェッチが行われる。
- コードがシンプルで、データの取得に関するロジックが明確。
- 状態管理(ローディング、エラーなど)が簡潔に扱える。
-
TanStack Queryを使わない場合:
- 状態管理やエラーハンドリングを自分で行う必要がある。
- コードが冗長になりがちで、複雑なロジックが必要になることがある。
TanStack Queryを使った場合と使わない場合の状態管理の違いについて説明します。
状態管理(TanStack Queryを使った場合)
TanStack Queryを使用すると、以下のような状態管理が自動的に行われます。
-
ローディング状態:
- データ取得中は
isLoading
がtrue
になります。これにより、簡単にローディングインジケーターを表示できます。
- データ取得中は
-
エラー管理:
- データ取得中にエラーが発生すると、
error
オブジェクトが返されます。これにより、エラーメッセージを簡単に表示できます。
- データ取得中にエラーが発生すると、
-
データのキャッシング:
- 取得したデータは内部的にキャッシュされ、同じクエリが再度実行される際には、キャッシュからデータが返されます。これにより、無駄なAPIリクエストを避けられます。
-
リファッチ(再取得):
- データが古くなった場合や、ウィンドウが再フォーカスされたときに自動でデータを再取得することができます。これにより、常に最新のデータを保持できます。
-
クエリの無効化と再有効化:
- 特定の条件でクエリを無効化したり再有効化したりすることが可能で、柔軟なデータ管理ができます。
状態管理(TanStack Queryを使わない場合)
TanStack Queryを使用しない場合は、状態管理を自分で行う必要があります。
-
ローディング状態:
-
useState
を使用してローディング状態を管理します。APIリクエストの開始時にloading
をtrue
に設定し、リクエスト完了後にfalse
に戻す必要があります。
-
-
エラー管理:
- 同様に、
error
という状態を作成し、APIリクエスト中にエラーが発生した場合にその状態を更新します。このため、エラーハンドリングのロジックを自分で実装する必要があります。
- 同様に、
-
データのキャッシング:
- データのキャッシングは自動では行われないため、毎回APIリクエストが行われます。これにより、不要にリクエストを行い、パフォーマンスが低下する可能性があります。
-
リフェッチ(再取得):
- ウィンドウがフォーカスされたときや、特定の条件で再取得を行うロジックを自分で実装する必要があります。これがないと、古いデータが表示され続けることになります。
-
状態管理の複雑さ:
- 状態管理のコードが複雑になりがちで、特に大規模なアプリケーションでは、複数のAPI呼び出しや状態を管理するのが難しくなります。
コード例
1. データ取得
import { useQuery } from '@tanstack/react-query';
const fetchUser = async () => {
const response = await fetch('https://api.example.com/user');
if (!response.ok) throw new Error('Network response was not ok');
return response.json();
};
const User = () => {
const { data, error, isLoading } = useQuery(['user'], fetchUser);
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>User Name: {data.name}</div>;
};
2. キャッシュ管理
const UserList = () => {
const { data } = useQuery(['users'], fetchUsers);
return (
<ul>
{data?.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
};
データはキャッシュされ、再度同じクエリを実行するとキャッシュから取得されます。
3. 自動再フェッチ
const UserProfile = () => {
const { data, isFetching } = useQuery(['user'], fetchUser, {
refetchOnWindowFocus: true,
});
return (
<div>
{isFetching && <span>Refreshing...</span>}
<h1>{data?.name}</h1>
</div>
);
};
ウィンドウがフォーカスされると自動でデータが再取得されます。
4. ローディング・エラーハンドリング
const Product = () => {
const { data, error, isLoading } = useQuery(['product'], fetchProduct);
if (isLoading) return <div>Loading product...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>Product Name: {data.name}</div>;
};
ローディング中とエラー発生時の表示を管理できます。
5. ページネーションや無限スクロール
const fetchPosts = async (page = 1) => {
const response = await fetch(`https://api.example.com/posts?page=${page}`);
return response.json();
};
const PostList = () => {
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
['posts'],
({ pageParam = 1 }) => fetchPosts(pageParam),
{
getNextPageParam: (lastPage) => lastPage.nextPage,
}
);
return (
<div>
{data.pages.map((page) =>
page.items.map(post => <div key={post.id}>{post.title}</div>)
)}
{hasNextPage && <button onClick={fetchNextPage}>Load More</button>}
</div>
);
};