😽

React Query-データ更新編

2022/08/01に公開

TanStack | High Quality Open-Source Software for Web Developers

React Query について公式ドキュメントを読みながら自分に必要なものをまとめたメモを記事化したもので以下の 4 つの記事で構成されています。記事ができたものから順次リンク化していきます。

データの更新 - useMutation

Mutations | TanStack Query Docs

useMutation を使う

const mutation = useMutation((userId) => deleteUser(userId))

return (
  <button onClick={() => mutation.mutate(userId)}>
    Delete User
  </button>
)

上記の mutation には mutate 以外にも useQuery とほぼ同じものが入っている

const { isLoading, isError, mutate, error } = useMutation(...)

data や error などの状態を初期化したい時は reset を使う

const { reset, error } = useMutation(...)
return (
  <span onClick={() => reset()}>{error.message}</span>
)

更新前後の処理を設定できる

const mutation = useMutation(..., {
  onMutate: (variables) => {
    // 更新関数実行前の処理
    // ロールバック処理に必要なデータなどを返せる
    return { oldData }
  },
  onError: (error, variables, context) => {
    // エラー発生時の処理
  },
  onSuccess: (data, variables, context) => {
    // 成功時の処理
  },
  onSettled: (data, error, variables, context) => {
    // 成功 / エラー問わず実行する後処理
  }
})

useMutation から受け取った mutete でも同様の設定が行え、こちらは useMutation 側のあとに実行される。

mutation.mutate(..., {
    onError: (error, variable, context) => {},
    onSuccess: (data, variable, context) => {},
    onSettled: (data, error, variables, context) => {},
})
  • 上記の各種コールバックの引数の意味は以下の通り
    • variables: mutate に渡した引数
    • data: 更新関数が返すデータ
    • error: エラー情報
    • context: onMutate で return したものが取得できる
      • 上記の例だと context.oldData が取得できる

同一の関数内などで連続して mutate が実行された場合、useMutation で指定した処理はその都度実行されるが、mutate で指定した処理は内部のオブザーバーに登録時に上書きされるので 1 度しか実行されない。

デフォルトでは失敗時にリトライしないので必要であれば指定する

useMutation(..., { retry: 3})

ネットワークエラー等で未完了な更新処理があるままアプリ終了した時に復元して実行する機能も備えているらしい(詳細はあとで必要時に調べる)

const state = dehydrate(queryClient)
hydrate(queryClient, state)
queryClient.resumuPauseMutations()

更新の反映

setQueryData によるデータの登録

更新時に受け取ったデータでキャッシュを更新する

const queryClient = useQueryClient()
useMutation(addUser, {
  onSuccess: (result) => {
    queryClient.setQueryData(['users'], old => [...old, result])
  }
})

setQueryData の第二引数は useState の set と同じように要素または関数を指定できる

queryClient.setQueryData(['users', userId], createdUser)

// 部分的に更新したい場合などに関数を使う
queryClient.setQueryData(['users', userId], (old) => {...old, user.name})
queryClient.setQueryData(['users'],
  (old) => old.map(
    user => user.id === result.id ? result : user
  )
)

Optimistic Updates | TanStack Query Docs
基本成功するだろうという前提で見た目上だけ先に反映させたい場合、onMutate で送信するデータを使って先に登録しておき、onSuccess で正しいデータに置き換えたり onError で巻き戻したりも出来る。

invalidateQueries による再取得トリガー

invalidateQueries は指定されたキャッシュの staleTime を上書きして期限切れにし、描画されているものであれば即時に再取得が行われる。

const queryClient = useQueryClient()
useMutation(addUser, {
  onSuccess: () => { queryClient.invalidateQueries(['users'])}
})

対象は配列の要素の前方一致で選ばれる

// 最初の要素が 'users' であれば以降に要素があっても全て対象となる
queryClient.invalidateQueries(['users'])
// 完全一致させたい時はオプションで指定する
queryClient.invalidateQueries(['users'], { exact: true })

関数を使ってフィルタリングすることもできる

queryClient.invalidateQueries({
  predicate: (query) =>
    query.queryKey[0] === 'users' && query.querykey[1].age < 20
})
// 下記のようなキャッシュキーが対象となり無効化される
useQuery(['users', { age: 18 }], ...)
  • predicate にフィルタ関数を指定する
  • フィルタ関数には Query が順番に渡され queryKey でキーの配列にアクセスできる
  • フィルタ関数が true を返した Query のキャッシュが無効化される

resetQueries によるキャッシュの削除

対象の指定方法は invalidateQueries 同様でこちらはキャッシュデータ自体を削除する

queryClient.resetQueries(['users'])

Discussion