TanStack Queryの仕組みをゼロから再構築:状態管理の設計思想を徹底理解する
本記事は、Reactで広く利用されるデータ取得ライブラリ「TanStack Query」(旧React Query)の内部動作を、ライブラリに頼らずゼロから実装することで、その設計思想を深く理解することを目的としています。単に便利なラッパーを作るのではなく、本家の仕組みを「地図」として把握することが狙いです。
まず、基本的なデータ取得の仕組みとして、`useState`と`useEffect`を用いた素朴な`useQuery`の実装から始めます。この段階では、ローカルステートにデータが閉じ込められるため、複数のコンポーネントが同じデータを参照しても、それぞれが独立した状態を持つという致命的な問題が指摘されます。この問題を解決するため、状態をコンポーネントの外、すなわち共有の置き場所に配置する必要があります。
次に、データ取得の最小単位である`Query`クラスを定義します。この際、状態管理の設計として、単なる「ローディング中/成功/エラー」の3軸だけでなく、「通信中(fetching)」と「アイドル(idle)」の2軸を設ける`fetchStatus`を導入することが重要だと説明されています。これにより、「古いデータを見せながら裏で更新中」という複雑な状態遷移を正確に表現できます。状態の更新ロジックは`reducer`関数に集約され、状態遷移の可視化とデバッグの容易さが確保されています。
さらに、状態を共有するための「倉庫」として`QueryCache`を導入します。`queryKey`(データ取得のキー)をそのまま使用すると参照比較の問題が生じるため、`hashKey`関数を用いてキーを安定した文字列(ハッシュ)に変換し、`Map`に格納します。`QueryCache`の`build()`メソッドは、「キーが存在すればそれを返し、なければ新しく作成して登録する」という`get-or-create`パターンを実現し、複数のコンポーネントからのアクセスで単一の`Query`インスタンスを保証します。
最後に、Reactとの橋渡し役として`QueryObserver`を導入します。これは、`Query`の状態変化を購読し、変化があった際にReactの再レンダリングをトリガーする「プッシュ機構」を担います。React側は`useSyncExternalStore`を利用することで、このオブザーバーを購読するだけで、状態の同期が実現します。この一連のステップを通じて、データ取得の「状態」をReactの外部(プレーンなJavaScriptクラス)に分離し、状態の共有と通知の仕組みを確立することが解説されています。
背景
TanStack Queryは、Reactアプリケーションにおけるサーバーサイドデータの状態管理を簡素化するために開発されたライブラリです。従来のReactのステート管理(useState/useContext)では、データ取得に伴うローディング、エラー、キャッシュ、再取得といった複雑なライフサイクルを扱うのが困難でした。本記事は、その内部の仕組みを理解するための技術解説記事です。
重要用語解説
- Query: データ取得の最小単位となるオブジェクト。データ(状態)と、そのデータを取得するロジック(queryFn)を一つにまとめたクラスであり、状態管理の核となる。
- QueryCache: 複数のコンポーネントからアクセスされるデータ(Query)を、一意のキー(queryKeyのハッシュ)に基づいて保管する「倉庫」の役割を果たすクラス。
- useSyncExternalStore: Reactのフックの一つで、外部のストア(状態管理の外部オブジェクト)の状態変化を効率的に購読し、コンポーネントを再レンダリングさせるために使用される。
- 影響: 本記事で解説されている設計パターン(状態の外部化、Observerパターン、キャッシュ機構)は、大規模なReactアプリケーションにおけるデータフローの理解を深め、より堅牢でスケーラブルな状態管理の実装能力を開発者に提供します。これにより、開発者はライブラリのブラックボックス化を避け、本質的な設計思想に基づいたコードを書くことが可能になります。