エフェクト(Effect)の視点で考える useQuery と useMutation
非同期処理の状態管理として絶大な支持を得ているTanstack Query
この記事では、ユーザインタラクションによるデータ取得処理におけるuseQuery
とuseMutation
の使い分けについて考察します。
まず、 useQuery
がReactのエフェクトとしての性質を持つと考えてみましょう。
React公式ドキュメントでは、エフェクトを次のように説明しています:
エフェクトは、特定のイベントによってではなく、レンダー自体によって引き起こされる副作用を指定するためのものです。
参照: https://ja.react.dev/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events
useQuery
は、コンポーネントのレンダーがトリガーとなり、外部データと同期を図ります。この動作は、まさに「レンダー自体によって引き起こされる副作用」に該当すると考えることができます。
また、エフェクトとイベントハンドラの違いについても次のように説明されています:
あるコードがエフェクトにあるべきか、イベントハンドラにあるべきかわからない場合は、そのコードが実行される理由を自問してください。コンポーネントがユーザに表示されたために実行されるべきコードにのみエフェクトを使用してください。
参照: https://ja.react.dev/learn/you-might-not-need-an-effect#sharing-logic-between-event-handlers
これに基づくと、useQuery
は「コンポーネントが表示されたときに実行されるべきコード」に該当します。一方で、「ユーザがボタンを押したときにデータを取得する」といった、特定のユーザインタラクションがトリガーとなる処理には、GETリクエストであってもuseMutation
を使う方が適しているのではないかと思いました。
以下は、ボタン押下時にサーバーからデータを取得し、表示するケースを考えた実装例です。
const useTodo = () => {
return useQuery({
queryKey: ["todo"],
queryFn: fetcher,
enabled: false,
});
};
export const Component = () => {
const { refetch, isFetching, data } = useTodo();
const handleClick = () => refetch();
return (
<div>
<button onClick={handleClick}>取得!</button>
<div>
{isFetching ? (
<p>loading...</p>
) : (
<>
{data?.map((d) => (
<div key={d.id}>
{d.id}: {d.title}
</div>
))}
</>
)}
</div>
</div>
);
};
注目するべきはuseQuery
のオプションにenabled
を指定していることです。
enabled
オプションをfalseに設定すると、useQuery
はコンポーネントのレンダー時に自動でデータ取得を実行しなくなります。代わりに、refetch
を実行することでデータ取得を制御できます。
このケースでは、ボタンクリック時にデータを取得する必要があるため、enabled: false
でレンダー時のフェッチを抑制するよりも、ユーザインタラクションに応じたデータ取得としてuseMutation
を使用する方が、処理の意図が明確になります。
const useTodo = () => {
return useMutation({
mutationFn: fetcher,
});
};
export const Component = () => {
const { data, isPending, mutate } = useTodo();
const handleClick = () => mutate();
return (
<div>
<button onClick={handleClick}>取得!</button>
...
ただし、useMutation
を使用する場合は、取得したデータがコンポーネント間でキャッシュ共有されない点などに注意が必要です。
useQuery
とuseMutation
はそれぞれの特性を活かした使い分けが重要です。データの再利用性、自動再検証、キャッシュ戦略などを考慮した上で、アプリケーションに適した選択をすることをお勧めします。
もしご意見や改善点がありましたら、ぜひコメントなどでご指摘ください。
Discussion