😸
React Queryのkey管理について
また React Query ネタになってしまった笑
問題提起
- key の間違いが起こる
mutate の後に例えば accounts を再取得する場合 invalidateQueries('accounts')
とすると accounts
に対して更新がかかる。
mutate > 再取得という流れを考えるとかなり簡単に簡潔にコードを書くことができる。
が、ここで渡す key が string
なので、key の間違いが起きてしまうという問題が起きてしまったり、目当ての key を検索などして探さなければならなくってしまう。
元の実装者が自分でなかったり、仮に前に実装したのが自分だとしても 3 日もすれば忘れてしまう。
やってみたこと
1 定数で key を管理する
QUERY_KEYS として定数化した。さらに型を定義しておく。
export const QUERY_KEYS = ["users", "post", "comments"] as const;
type Unpacked<T> = T extends { [K in keyof T]: infer U } ? U : never;
export type QueryKeysTypes = Unpacked<typeof QUERY_KEYS>;
2 カスタム queryClient
React Query の queryClient を Wrapper してカスタムする。queryClient 自体は他にも色々メソッドがあるが、今回は省略している。必要なものを return する感じで。
第一引数として key を渡す。そこで先程の QueryKeysTypes
で型ガードしておく。
export function useQueryClientWrapper(): {
invalidateQueries: (k: QueryKeysTypes, deps?: QueryKey) => void;
} {
const queryClient = useQueryClient();
const invalidateQueries = useCallback(
(key, deps) => {
const k = Array.isArray(deps) ? [key, ...deps] : [key];
queryClient.invalidateQueries(k);
},
[queryClient]
);
return { invalidateQueries };
}
3 useQueryWrapper にも同様に型定義しておく
以前書いた記事の useQueryWrapper の queryKey に対して QueryKeysTypes を適用する。
type Props<T> = {
queryKey?: QueryKeysTypes;
deps?: QueryKey;
options?: UseQueryOptions;
req: () => Promise<AxiosResponse<T>>;
};
export const useQueryWrapper = <T>({
queryKey,
deps = [],
options,
req,
}: Props<T>): UseQueryResult<T> => {
const k = Array.isArray(deps) ? [queryKey, ...deps] : [queryKey];
const result = useQuery(
k,
async () => {
try {
const res = await req();
return res.data;
} catch (e) {
// ここでトースト出したり
throw e.error;
}
},
options
) as UseQueryResult<T>;
return result;
};
まとめ
これで型安全にできるし、エディターでサジェストされるようになる。
以下のようにエンドポイントごとの Query を作る際もサジェストされるようになっている。
なお、エンドポイントごとの Query については以前の記事をみていただけると!!
Discussion