🌏

【翻訳】RTK Query Overview

2021/09/20に公開

この記事について

https://redux-toolkit.js.org/rtk-query/overview
RTK Query が良さげだったので、Redux Toolkit公式ドキュメント内の概要を翻訳しました。
原文は上記のURLをご確認ください。

この記事に含まれること

  • RTK Queryとは何か、どのような問題を解決するのか
  • RTK Queryに含まれるAPI
  • RTK Queryの基本的な使い方

RTK Queryは、強力なデータフェッチ&キャッシュツールです。RTK Queryは、Webアプリケーションでデータを取得する際の一般的な手法を簡素化するように設計されています。もはやデータフェッチ&キャッシュロジックを自分で手書きする必要はありません。

RTK Queryは、Redux Toolkitパッケージに含まれるアドオンであり、その機能はRedux Toolkitの他のAPIの上に構築されています。

なぜ必要なのか

Webアプリケーションでは通常、データを表示するためにサーバーからデータを取得する必要があります。また、データを更新してサーバーに送信し、クライアントにキャッシュされたデータとサーバー上のデータを同期させる必要があります。
近頃のアプリケーションでは

  • スピナーを表示するための読み込み状態の追跡
  • 同じデータに対する重複したリクエストの回避
  • UIの高速化のための最適化された更新
  • ユーザーがUIを操作する際のキャッシュの寿命の管理

といった動作も求められるため、実装はより複雑なものになっています。

Reduxのコアは非常にミニマルであり、実際のロジックはすべて開発者が書くことになっています。そのため、Reduxにはこのようなユースケースを解決するための機能が組み込まれていません。

Reduxのドキュメントでは、読み込み状態やリクエストの結果を追跡するために、リクエストのライフサイクルにおいてactionをdispatchするための一般的なパターンが教えられています。Redux ToolkitのcreateAsyncThunk APIはその典型的なパターンを抽象化するために設計されました。
しかし、ユーザーは依然として、読み込み状態やキャッシュされたデータを管理するために、かなりの量のReducerロジックを書かなければなりません。

ここ数年、Reactコミュニティは、「データの取得とキャッシュ」は「状態管理」とは異なる関心事であると理解してきました。
Reduxのような状態管理ライブラリを使用してデータをキャッシュすることはできます。ですが、ユースケースの違いを考慮に入れると、データフェッチ専門のツールを使用する価値は十分にあるでしょう。

RTK Queryは、Apollo Client、React Query、Urql、SWRなど、データフェッチのより良い手法を開拓してきたツール達からインスピレーションを得ています。しかし、そのAPI設計には下記のような独自のアプローチを加えています。

  • データフェッチとキャッシュのロジックは、Redux ToolkitのcreateSliceとcreateAsyncThunk APIの上に構築されています。
  • Redux ToolkitはUIに依存しないため、RTK Queryの機能はどのようなUIレイヤーでも使用できます。
  • APIエンドポイントは、引数からクエリパラメータを生成する方法や、キャッシュのためにレスポンスを変換する方法などを含めて、事前に定義されています。
  • RTK Queryには、下記の機能も含まれます
    • データ取得プロセスをカプセル化するReact hooksの生成
    • コンポーネントへのデータおよびisLoadingフィールドの提供
    • コンポーネントのマウントおよびアンマウントに伴うキャッシュのライフタイム管理
  • RTK Queryには、"cache entry lifecycle "オプションが存在します。それにより、最初のデータを取得した後、websocketメッセージを介してキャッシュの更新をストリーミングするようなユースケースを可能にします。
  • OpenAPIやGraphQLのスキーマからAPI sliceを自動的に生成する機能があります(試験版)。
  • 最後に、RTK Queryは完全にTypeScriptで記述されており、優れたTS使用体験を提供するように設計されています。

何が含まれるのか

APIs

RTK Queryは、Redux Toolkitのコアパッケージに含まれており、以下の2つのエントリーポイントのいずれかから利用できます。

import { createApi } from '@reduxjs/toolkit/query'

/* 定義されたエンドポイントに対応するフックを自動的に生成する、
  React固有のエントリーポイント */
import { createApi } from '@reduxjs/toolkit/query/react'

RTK Queryには、以下のAPIが含まれます。

createApi()

RTK Queryの機能の中核となるものです。
データ取得時の設定や変換方法も含め、各エンドポイントからのデータ取得方法をまとめて定義できます。ほとんどの場合、1つのアプリケーションで一度だけ用いるべきです。「1つのベースURLにつき1つのAPI slice」が目安となります。

fetchBaseQuery()

リクエストの簡素化を目的とした、ごく小さなfetchのラッパーです。多くのユーザーが createApiで使用する、推奨ベースクエリーとして意図されています。

<ApiProvider />

storeを作成していない場合、こちらをProviderとして利用できます。

setupListeners()

refetchOnMountおよびrefetchOnReconnectを有効にするためのユーティリティです。

バンドルサイズ

RTK Queryにより、アプリのバンドルサイズは一定量増加します。
RTK QueryはRedux ToolkitとReact-Redux上に構築されているため、増加するサイズは、既にそれらを使用しているかどうかによって異なります。推定されるmin+gzipのバンドルサイズは以下の通りです。

  • すでにRTKを使用している場合
    RTK Query:約9kb + hooks:約2kb
  • RTKを使用していない場合
    • Reactを使わない場合
      RTK + dependencies + RTK Query:約17kB
    • Reactを使用している場合
      19kB + React-Redux(peerDependencies)

エンドポイントの定義を追加した際、バンドルサイズの増加量は実際のコードに基づきます。通常だと数バイト程度です。
RTK Queryに含まれる機能は、追加されたバンドルサイズに見合うだけのものです。手書きのデータ取得ロジックを排除することで、ほとんどのアプリケーションではバンドルサイズが改善されるはずです。

基本的な使い方

API Sliceを作成する

RTK Queryは、Redux Toolkitのコアパッケージに含まれており、以下の2つのエントリーポイントのいずれかから利用できます。

import { createApi } from '@reduxjs/toolkit/query'

/* 定義されたエンドポイントに対応するフックを自動的に生成する、
  React固有のエントリーポイント */
import { createApi } from '@reduxjs/toolkit/query/react'

Reactでの典型的な使用方法としては、まずcreateApiをimportし、サーバーのベースURLとどのエンドポイントとやり取りしたいかを記載した「API slice」を定義します。

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { Pokemon } from './types'

// Define a service using a base URL and expected endpoints
export const pokemonApi = createApi({
  reducerPath: 'pokemonApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }),
  endpoints: (builder) => ({
    getPokemonByName: builder.query<Pokemon, string>({
      query: (name) => `pokemon/${name}`,
    }),
  }),
})

// Export hooks for usage in functional components, which are
// auto-generated based on the defined endpoints
export const { useGetPokemonByNameQuery } = pokemonApi

Storeを設定する

また、「API Slice」には、自動生成されたRedux slice reducerと、購読時のライフタイムを管理するカスタムミドルウェアが含まれています。これらはどちらもRedux Storeに追加する必要があります。

import { configureStore } from '@reduxjs/toolkit'
// Or from '@reduxjs/toolkit/query/react'
import { setupListeners } from '@reduxjs/toolkit/query'
import { pokemonApi } from './services/pokemon'

export const store = configureStore({
  reducer: {
    // Add the generated reducer as a specific top-level slice
    [pokemonApi.reducerPath]: pokemonApi.reducer,
  },
  // Adding the api middleware enables caching, invalidation, polling,
  // and other useful features of `rtk-query`.
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(pokemonApi.middleware),
})

// optional, but required for refetchOnFocus/refetchOnReconnect behaviors
// see `setupListeners` docs - takes an optional callback as the 2nd arg for customization
setupListeners(store.dispatch)

コンポーネント内でhooksを使う

最後に、API Sliceから自動生成された React hooksをコンポーネントファイルにimportし、必要なパラメータを付与した上でhooksを呼び出します。RTK Queryは、マウント時に自動的にデータを取得し、パラメータが変更されたタイミングで再取得し、結果に{data, isFetching}の値を提供し、これらの値が変更されるとコンポーネントを再レンダリングします。

import * as React from 'react'
import { useGetPokemonByNameQuery } from './services/pokemon'

export default function App() {
  // Using a query hook automatically fetches data and returns query values
  const { data, error, isLoading } = useGetPokemonByNameQuery('bulbasaur')
  // Individual hooks are also accessible under the generated endpoints:
  // const { data, error, isLoading } = pokemonApi.endpoints.getPokemonByName.useQuery('bulbasaur')
  
  // render UI based on data and loading state
}

その他の情報

RTK Query Quick Start チュートリアルでは、Redux Toolkit を使用するプロジェクトに RTK Query を追加する方法、エンドポイント定義を含む「API スライス」を設定する方法、自動生成された React フックをコンポーネントで使用する方法などの例を紹介しています。

RTK Queryの使い方ガイドのセクションでは、データの問い合わせMutationを使ったサーバーへの更新送信キャッシュ更新のストリーミングなどのトピックに関する情報が掲載されています。

Examplesページでは、GraphQLによるクエリの作成認証Svelteのような他のUIライブラリとRTK Queryの併用などのトピックのデモとして、実行可能なCodeSandboxが用意されています。

Discussion