Closed2

React Query、内部処理の結果をキャッシュするメモ

niccariniccari

TanStack Queryをメインで用いるとき、主には以下のユースケースになる。

  • 基本的にはuseQueryを使って外部APIを呼び出し、得られたデータをキャッシュする。
  • キャッシュの有効期限を制御する(cacheTime)
    • この時間が経つまで、事前に取得したデータのキャッシュを利用できる。この時間を超えていたら、データ取得+キャッシュの更新を行う
  • Stale-While-Revalidateを行う(staleTime)
    • 前回のデータ取得からstaleTime経過していたら、アクセス直後からバックグラウンドでデータ取得を行う(このとき、キャッシュが有効であればキャッシュをそのまま返す。取得したデータがキャッシュから使えるようになるのは次のアクセス時)
  • データの更新・削除を行う際、useMutationを使う
    • 処理が成功していたら、キャッシュを無効化する。データの更新・削除に伴い、キャッシュが古くなっているため。
  • キャッシュはQueryKeyによって管理される。
    • e.g. ["todos"]、["todos", "1"]、["todos", "1", "subtodos"]、["todos", "1", "subtodos", "a"]
      • 以前はstringを入れられていたが、v4時点ではarrayでないとNG。
niccariniccari

TanStack Queryを状態管理として使うとしたとき、非同期な内部処理を実行し、結果を保存しておきたいことがある。
この場合、例えば以下の様にカスタムクエリを作ることができる。ここでは、何らかのパラメータに基づいて数値シミュレーションを行い、その結果をキャッシュすることを想定している。

const CACHE_KEY = ["SimulationResult"];

export const useMathematicalSimulation = (): {
  simulationResult: SimulationResult;  // SimulationParams含め、型は適宜定義すること
  error: Error | null;
  isLoading: boolean;
  runSimulation: (params: SimulationParams) => void;
} => {
  const queryClient = useQueryClient();    // App.tsxなどで、<QueryClientProvider>の追加が必要
  const getInitialResult = () => null;    // cacheTime = infinityなので、useQueryの初回実行時しか呼ばれない。useQueryではqueryFnが必須なので便宜的に定義している
  const { data, error, isLoading } = useQuery<SimulationResult, Error>(CACHE_KEY, getInitialResult, {
    initialData: null,    // simulationResultの型をNonNullに保つために必要
    cacheTime: Infinity,    // runSimulationを呼ばない限り、キャッシュを残したい
    staleTime: Infinity,      // キャッシュを自動で無効化する必要はない
  });

  const update = async (params: SimulationParams): Promise<SimulationResult> => {
    /* 内部処理をここで行う。以下のsimulationResultは内部処理の結果の想定 */
    return simulationResult;
  };

  const { mutate } = useMutation((params: SimulationParams) => update(params), {
    onSuccess: (simulationResult) => {
      queryClient.setQueryData(CACHE_KEY, simulationResult);
    },
    onError: (err) => {
      /* 内部処理が失敗したときの処理を必要に応じ書く */
    },
  });
  return { simulationResult: data, error, isLoading, runSimulation: mutate };
};

あとはcomponent側で、以下の様に呼び出して使う。

const SimulationPage: (props: Props) => JSX.Element = () => {
  const { simulationResult, isLoading, error, runSimulation } = useMathematicalSimulation();

  return (
    <div>
      {isLoading && (/* ローディング時の表示 */)}
      {error && (/* エラーがあるときの表示 */)}
      ...
      <SomeComponent onSubmit={(params) => runSimulation(params)} />
      ...
    </div>
  );
}
このスクラップは2022/11/13にクローズされました