OITA: Oika's Information Technological Activities

@oika 情報技術的活動日誌。

react-query:invalidate と reset の挙動の違い

お勉強メモ。嘘ついたらごめんなさい。
React Query で、cacheTime, staleTime と、invalidateQueries, resetQueries あたりの使い分けの話。

お試し用コードは最後に載せてます。

cacheTime, staleTime について

QueryClient には以下の設定がある。

  • cacheTime : 取得済みのデータ(以下「キャッシュ」と呼ぶ)を保持する時間(デフォルト5分)
  • staleTime : キャッシュが古くなったと見なして再取得する時間(デフォルト0)

以下のようなコンポーネントの場合、

const SomeComponent = () => {
  const { data, isLoading } = useQuery("foo", fetchData);

  if (isLoading) return <div>Loading...</div>

  return <div>{data.something}</div>
}

初回の表示時はキャッシュがないので、データの取得中は「Loading...」が表示される。

2回目以降のデータ再取得時は*1、以下のように動く。

  • 前回データ取得時から cacheTimestaleTime も経過していなければ、キャッシュを表示して終わり
  • 前回データ取得時から cacheTime は経過せず、 staleTime が経過していれば、キャッシュを表示した上で、バックグラウンドで最新のデータを取り直し、取得できた時点で画面へ反映する
  • 前回データ取得時から cacheTime が経過していれば、キャッシュは破棄し、「Loading...」を表示した状態でデータ取得を待機する

つまりデフォルトでは、5分以内に画面を再表示した際は、とりあえず取得済みのキャッシュを表示した状態で、常にバックグラウンドでキャッシュを最新データにリフレッシュする動きとなる。

このあたりの話はDoc の Important Defaults に書いてある。

isLoading と isFetching

上記の補足で、 useQuery のレスポンスには isLoadingisFetching が含まれている。
違いは以下。

  • isLoading : キャッシュにデータがなく、データ取得中の状態
  • isFetching : キャッシュにデータがある場合も含め、データ取得中の状態

なので通常は、上のコード例のとおり、 isLoading を見て表示に反映することになると思う。

invalidateQueries と resetQueries

画面から更新したデータを再取得するようなケースでは、取得済みのキャッシュを明示的に破棄したい。

このとき、 QueryClient のインスタンスに対して、 invalidateQueries または resetQueries をコールすることができる。
違いは以下。

  • invalidateQueries : キャッシュが古くなったとみなす( =staleTime が経過した状態
  • resetQueries : キャッシュを破棄する( =cacheTime が経過した状態

公式 Doc を読む限り、データ更新後は invalidateQueries する使い方を想定してるのかなと感じたが、例えば画面から書き込んだデータをすぐに画面に表示するようなケースだと、再fetchが終わるまで、更新前の古いデータ(キャッシュ)が表示されるような動きになってしまうのが微妙だと思う。

最新データが再取得されるまで画面に古いデータを表示したくないときは、 resetQueries のほうが使い勝手が良いかも。

以上。reset と invalidate の挙動は以下で試せます。

*1:データ再取得のトリガは refetchOnMount, refetchOnWindowFocus, refetchOnReconnect, refetchInterval のオプションで制御する