Zustand + TanStack Query を組み合わせた実践的な使い方って、どんなものがあるのかな?
今回は、React.js で クライアント側(UI)の状態を管理する Zustand と、API から取得するサーバーデータを管理する TanStack Query(旧 React Query) を組み合わせた実践的な使い方をわかりやすく解説します。
※ React.js の環境構築がまだお済みでない場合は、下記より環境構築を行なってください。
Zustand とは?
Zustand について、下記に詳しく記載しましたので、ご確認ください。
TanStack Query(旧 React Query) とは?
TanStack Query(旧 React Query)について、下記に詳しく記載しましたので、ご確認ください。
Zustand + TanStack Query と連携したユースケース
Zustand + TanStack Query の構成
役割分担
Zustand と TanStack Query を組み合わせる場合は、それぞれの役割を明確に分けることが重要です。
- Zustand
フォームの入力値 や モーダルの開閉、ダークモードのON/OFF などの クライアント(UI)の状態を管理 します。即時に変更される一時的な状態の管理に適しています。 - TanStack Query
ユーザー一覧データ や 投稿データ、商品一覧データ などの、サーバーから取得したデータ(サーバーステート) を管理 します。データの取得・キャッシュ・再取得・ローディング管理 などを 自動で行ってくれます。
メリット
Zustand と TanStack Query を組み合わせる最大のメリットは、サーバーデータの取得・キャッシュ・再取得は TanStack Query、クライアント(UI)の状態管理は Zustand と、それぞれの役割を明確に分離できる ことです。
その結果、状態の変更範囲が限定 され、不要な再レンダリングを防ぎやすく なります。さらに、パフォーマンスが向上し、保守性・可読性の高いコードを維持しやすく なります。
Zustand + TanStack Query のコード例
Store の作成 ※ Zustand(UI状態管理)
ユーザー情報のUI状態 の 状態(データ)の初期値 や 処理内容 を登録する
// ZustandStore.js
import { create } from 'zustand'
export const useUserUIStore = create((set) => ({
selectedUserId: null,
isModalOpen: false,
selectUser: (id) => set({ selectedUserId: id }),
openModal: () => set({ isModalOpen: true }),
closeModal: () => set({ isModalOpen: false }),
}));
QueryClientProvider の設定 ※ TanStack Query(データ取得状態管理)
TanStack Query を使えるように、アプリ全体で QueryClient を設定する
// App.jsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ZustandTanStackQueryUserList from './ZustandTanStackQueryUserList';
const queryClient = new QueryClient();
function App() {
return (
<QueryClientProvider client={queryClient}>
<ZustandTanStackQueryUserList />
</QueryClientProvider>
)
}
export default App
データの取得・表示・更新
アプリ上 で、状態(データ)の取得・表示・更新 を確認する
- ユーザーをクリック
- Zustandが更新 され、Reactが再レンダリング される
- ”isModalOpen && selectedUser && (…)” の条件が true になり、モーダルが表示 される
ユーザー一覧取得 などの データ取得処理 : TanStack Query
選択中ID や モーダル状態 などの UI状態 : Zustand
// ZustandTanStackQueryUserList.jsx
import { useUserUIStore } from './ZustandStore.js';
import { useQuery } from '@tanstack/react-query';
// ユーザーリストの取得処理
const fetchUsers = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users/');
if (!res.ok) {
throw new Error('Failed to fetch users');
}
return res.json();
};
export default function ZustandTanStackQueryUserList() {
// useQuery: APIからデータを取得する処理と状態管理
const {
data: users,
isLoading,
error,
} = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
});
// Zustand から状態を取得
const selectedUserId = useUserUIStore((state) => state.selectedUserId); // 洗濯中のユーザー
const isModalOpen = useUserUIStore((state) => state.isModalOpen); // モーダルの状態
const selectUser = useUserUIStore((state) => state.selectUser); // ユーザーの選択処理
const openModal = useUserUIStore((state) => state.openModal); // モーダルのオープン処理
const closeModal = useUserUIStore((state) => state.closeModal); // モーダルのクローズ処理
if (isLoading) return <p>Loading...</p>; // ローディング中
if (error) return <p>Error occurred</p>; // エラー
// 選択中のユーザーを取得
const selectedUser = users?.find((user) => user.id === selectedUserId);
return (
<>
<h2>Zustand TanStackQuery UserList</h2>
{/* ユーザーリスト */}
{users?.map((user) => (
<div
key={user.id}
onClick={() => {
selectUser(user.id);
openModal();
}}
// 選択中のユーザの背景色が水色になる
style={{
cursor: 'pointer',
marginBottom: '8px',
padding: '4px',
backgroundColor: selectedUserId === user.id ? '#d0ebff' : 'white',
}}
>
{user.name}
</div>
))}
{/* モーダル: 選択したユーザーの詳細をモーダル表示 */}
{isModalOpen && selectedUser && (
<div
style={{
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
background: 'rgba(0,0,0,0.3)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
}}
>
<div
style={{
background: 'white',
padding: '20px',
borderRadius: '8px',
width: '300px',
}}
>
<h3>{selectedUser.name}</h3>
<p>Email: {selectedUser.email}</p>
<p>Phone: {selectedUser.phone}</p>
<button onClick={closeModal}>閉じる</button>
</div>
</div>
)}
</>
);
}
処理内容
- “画面をリロード(更新処理)
- 一瞬 “Loading…” の文字が表示され、複数のユーザー名がリスト表示 される
- ユーザーリスト から ユーザー名をクリック すると、ユーザー名の背景色が水色になり、ユーザーの詳細情報のモーダルが表示される
- 閉じる ボタンで、ユーザーリスト 画面に戻る
- また ユーザーリスト から 別な ユーザー名をクリック すると、別な ユーザー名の背景色が水色になり、ユーザーの詳細情報のモーダルが表示される

まとめ
Zustand と TanStack Query を組み合わせた実践的な使い方について解説してきましたが、いかがでしたでしょうか?
Zustand は、フォームの入力値 や モーダルの開閉、ダークモードのON/OFF などの クライアント(UI)の状態を管理 し、TanStack Query は、ユーザー一覧 や 投稿データ、商品一覧 などの サーバーから取得したデータ(サーバーステート) を管理 してくれます。
この 2 つを組み合わせることで、サーバーデータの取得・キャッシュ・再取得は TanStack Query、クライアント(UI)の状態管理は Zustand と、それぞれの役割を明確に分離する ことができます。
まずは小さな コンポーネント から実践し、Zustand と TanStack Query を組み合わせながら、適切に使い分けられるようになっていきましょう。
公式サイト






コメント