TanStack Query の invalidateQueries() や refetchQueries() がうまく動作しない時に確認すること

# きっかけ

`useMutation`したタイミングで、`invalidateQueries`を利用して、関連するクエリを再実行し状態を更新しようとしたところ、全然できず詰まってしまった。

このスレに救われたので、ここに載っていた間違い集をまとめておく。

https://stackoverflow.com/questions/68577988/invalidate-queries-doesnt-work-react-query

# 確認した方が良いところ

## 1. 複数のQueryCleintインスタンスを作成していないか

基本的にQueryClientのインスタンスはアプリケーションライフサイクルにおいて、一つだけ作成すべき。
誤って複数のQueryCleintインスタンスを作成していないか確認しよう。

https://tanstack.com/query/latest/docs/eslint/stable-query-client

```tsx:NG例 ❌
/* eslint "@tanstack/query/stable-query-client": "error" */

function App() {
  const queryClient = new QueryClient()
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
```

```tsx: OK例1 ⭕️
function App() {
  const [queryClient] = useState(() => new QueryClient())
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
```

```tsx: OK例2 ⭕️
const queryClient = new QueryClient()
function App() {
  return (
    <QueryClientProvider client={queryClient}>
      <Home />
    </QueryClientProvider>
  )
}
```

```tsx: OK例3 ⭕️
async function App() {
  const queryClient = new QueryClient()
  await queryClient.prefetchQuery(options)
}
```

## 2. `enabled`オプションが`false`になっているクエリに対しては、`invalidateQueries`や`refetchQueries`は使えない

それはそうという感じだが、一応確認しておこう。

https://tanstack.com/query/latest/docs/framework/react/guides/disabling-queries

```tsx:NG例 ❌
function Todos() {
  const { data, isFetching } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
    // enabled=falseのクエリは、invalidateQueries()やrefetchQueries()の影響を受けない
    enabled: false,
  })

  return (...)
}
```

## 3. QueryClientインスタンスへのアクセスは`useQueryClient`を使う

`QueryClientProvider`を利用している場合は、`useQueryClient`フックを利用してQueryClientインスタンスにアクセスしよう。

https://tanstack.com/query/latest/docs/framework/react/reference/useQueryClient#usequeryclient

```tsx:NG例 ❌
const createTodoMutation = useMutation(postTodo, {
  onSuccess: async () => {
    const queryClient = new QueryClient();
    await queryClient.invalidateQueries("todos");
  },
});
```

```tsx:OK例 ⭕️
import { useQueryClient } from '@tanstack/react-query'

const queryClient = useQueryClient()

const createTodoMutation = useMutation(postTodo, {
  onSuccess: async () => {
    await queryClient.invalidateQueries("todos");
  },
});
```

## 4. `queryKey`が誤っている

`invalidateQueries`や`refetchQueries`に渡している`queryKey`が誤っていないか確認しよう。

## 5. `invalidateQueries`や`refetchQueries`が実行される前に画面遷移したり、コンポーネントをアンマウントしてしまっている

```tsx:NG例 ❌
import { useQueryClient } from '@tanstack/react-query'

const queryClient = useQueryClient()

const createTodoMutation = useMutation(postTodo, {
  onSuccess: async () => {
    // ここで画面遷移してしまうと、後続の処理が実行されない
    // コンポーネントのアンマウントも同様（ダイアログの開閉など要注意）
    router.push("/todos")
    await queryClient.invalidateQueries("todos");
  },
});
```

## 6. `invalidateQueries`しようとしているクエリが`inactive`である

`invalidateQueries`は、デフォルトだと`active`なクエリのみをinvalidateするため、
クエリが`inactive`な状態である場合（画面遷移した時やコンポーネントがアンマウントされた場合など）は、対象に含まれない。
`inactive`なものも対象に含める場合は、`refetchType`オプションを利用する必要がある。

https://tanstack.com/query/latest/docs/reference/QueryClient#queryclientinvalidatequeries