1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

TanstackQuery-useQuery基礎

Last updated at Posted at 2024-07-30

TanstackQueryとは?

参考:

Installation | TanStack Query React Docs

Web アプリケーションでの

  • サーバー状態の取得(フェッチ)
  • キャッシュ
  • 同期、サーバーステートの更新

を簡単に扱えるようにするライブラリである

インストール

npm i @tanstack/react-query

useQuery

  • 基本的なデータ取得に使用するフック
  • queryKey(一意のキー)とqueryFn(任意のPromiseベースのメソッド)を引数にとる
  • サーバー上のデータを変更する場合はuseQueryではなくuseMutationsを使用した方がいい
  • useQueryによって返されるクエリ結果には、テンプレート作成やデータのその他の使用に必要なクエリに関するすべての情報が含まれる
import { useQuery } from '@tanstack/react-query'

function App() {
  const info = useQuery({ queryKey: ['todos'], queryFn: fetchTodoList })
}

// isPendingまたはstatus === 'pending' - クエリにはまだデータがない
// isErrorまたはstatus === 'error' - クエリでエラーが発生
	// error - クエリがisError状態の場合、エラーはerrorプロパティを介して利用できる
// isSuccessまたはstatus === 'success' - クエリは成功し、データは利用可能
	// data - クエリがisSuccess状態の場合、データはdataプロパティを介して利用できる
// isFetching - どのような状態でも、クエリがいつでもフェッチ中の場合 (バックグラウンドの再フェッチを含む)、isFetching はtrueになる。
  • ほとんどのクエリでは、 isPending状態をチェックし、次にisError状態をチェックし、最後にデータが利用可能であると想定して成功状態をレンダリングするだけで十分である
function Todos() {
  const { isPending, isError, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })

  if (isPending) {
    return <span>Loading...</span>
  }

  if (isError) {
    return <span>Error: {error.message}</span>
  }

  // We can assume by this point that `isSuccess === true`
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}

// ステータス状態でも同じことが可能
function Todos() {
  const { status, data, error } = useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodoList,
  })

  if (status === 'pending') {
    return <span>Loading...</span>
  }

  if (status === 'error') {
    return <span>Error: {error.message}</span>
  }

  // also status === 'success', but "else" logic works, too
  return (
    <ul>
      {data.map((todo) => (
        <li key={todo.id}>{todo.title}</li>
      ))}
    </ul>
  )
}
  • フェッチステータス
    • ステータスフィールドに加えて、次のオプションを持つ追加のfetchStatusプロパティも取得できる

    • fetchStatus === 'fetching'

      • フェッチ中
    • fetchStatus === 'paused'

      • フェッチしようとしたが、一時停止された
    • fetchStatus === 'idle'

      • 実行されていない

ステータスとフェッチステータスの責務

  • 2つのステータスを利用してフェッチとデータの状態を組み合わせ管理できる
    • ステータスはデータがあるかどうか?などデータに関する情報である
    • フェッチステータスはqueryFnが実行中かどうか?に関する情報である

queryKey

  • クエリキーに基づいてアプリケーション全体でクエリの再取得、キャッシュ、共有を行うために内部的に使用される。
  • トップレベルは配列の必要があるが、1つの文字列を含む配列のように単純なものから、多数の文字列とネストされたオブジェクトの配列のような複雑なものでもクエリのデータに対して一意であればなんでも良い
// オブジェクト内のキーの順番などは考慮しないため、下記は全て同一のqueryKeyとしてみなされる
useQuery({ queryKey: ['todos', { status, page }], ... })
useQuery({ queryKey: ['todos', { page, status }], ...})
useQuery({ queryKey: ['todos', { page, status, other: undefined }], ... })

// 配列においては順序を考慮するため、下記は別のqueryKeyとみなされる
useQuery({ queryKey: ['todos', status, page], ... })
useQuery({ queryKey: ['todos', page, status], ...})
useQuery({ queryKey: ['todos', undefined, page, status], ...})

queryKeyを一意に保たない場合は、同じ箇所を参照しようとするため、表示されるデータが意図せず同一のものになってしまったり、不用意にキャッシュを使用してしまったりするため、必ず一意にすること

queryFn

  • クエリ関数はPromiseを返す任意の関数となる
  • 返されるPromiseはデータを返すか、エラーをスローする必要がある
// 有効な記載方法一覧
useQuery({ queryKey: ['todos'], queryFn: fetchAllTodos })
useQuery({ queryKey: ['todos', todoId], queryFn: () => fetchTodoById(todoId) })
useQuery({
  queryKey: ['todos', todoId],
  queryFn: async () => {
    const data = await fetchTodoById(todoId)
    return data
  },
})
useQuery({
  queryKey: ['todos', todoId],
  queryFn: ({ queryKey }) => fetchTodoById(queryKey[1]),
})

エラー処理とスローについては、クエリ関数がrejectされたPromiseをスローするか返す必要がある。これらはエラーとしてクエリに保持される

const { error } = useQuery({
  queryKey: ['todos', todoId],
  queryFn: async () => {
    if (somethingGoesWrong) {
      throw new Error('Oh no!') // throwするか
    }
    if (somethingElseGoesWrong) {
      return Promise.reject(new Error('Oh no!')) // Promise.rejectを返すか
    }

    return data
  },
})

queryKeyからqueryFnへContextとしてデータを渡す

  • queryKeyはQueryFunctionContext の一部としてクエリ関数に渡すこともできる
function Todos({ status, page }) {
  const result = useQuery({
    queryKey: ['todos', { status, page }],
    queryFn: fetchTodoList,
  })
}

// Access the key, status and page variables in your query function!
function fetchTodoList({ queryKey }) {
  const [_key, { status, page }] = queryKey
  return new Promise()
}

QueryFunctionContext

  • 各クエリ関数へ渡されるオブジェクトである。下記の要素で構成されている
    • queryKey: QueryKey
      • クエリキー
    • signal: AbortSignal?
      • シグナル(非同期操作 (フェッチ要求など) と通信し、必要に応じてオブジェクト経由で操作を中止できるようにするシグナル オブジェクト⇒クエリキャンセルに使用)
    • meta: Record | undefined
      • クエリに関する追加情報を入力できるオプションのフィールド

以上

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?