Redux Toolkit の RTK Query で REST API を使う

前回は createAsyncThunk を使いましたが、チュートリアルを読み進んでみると、元々データフェッチの機能が用意されていましたので、今回はそちらの方法で REST API を使ってみました。
create-react-app でテンプレートを用意する所は前回と同じなので、その後の修正部分を書いていきます。

REST API を使用するコードを追加

公式の例を参考に createApi、fetchBaseQuery を使って書きます。
データを取得したい側は、useGetCityQuery フックを使うようになります。

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

export type city = {
  status: string;
  data: {
    id: string,
    name: string,
  }[];
};

export const cityApi = createApi({
  reducerPath: 'cityApi',
  baseQuery: fetchBaseQuery({ baseUrl: 'https://www.land.mlit.go.jp/webland/api/' }),
  endpoints: (builder) => ({
    getCity: builder.query<city, string>({
      query: (code) => `CitySearch?area=${code}`,
    }),
  }),
})

export const { useGetCityQuery } = cityApi

表示部分を次のように追加しました。

import React, { useState } from 'react';
import { useGetCityQuery } from '../../app/services/city';

export function City() {
  const [inputCode, setInputCode] = useState('13');
  const [code, setCode] = useState('');
  const { data, error, isLoading } = useGetCityQuery(code, { skip: code === ''});
  const cities = (code === '')
    ? undefined
    : data?.data;
  return (
    <div>
      <input type="text" value={inputCode} onChange={(e) => setInputCode(e.target.value)} />
      <button onClick={() => setCode(inputCode)}>get</button>
      <button onClick={() => { setCode(''); setInputCode(''); }}>clear</button>
      {
        error
        ? 'Error.'
        : isLoading
        ? 'Loading...'
        : <table>
            <thead>
              <tr>
                <th>ID</th>
                <th>name</th>
              </tr>
            </thead>
            <tbody>
              {
                cities?.map(c => 
                  <tr key={c.id}>
                    <td>{c.id}</td>
                    <td>{c.name}</td>
                  </tr>
                  )
              }
            </tbody>
          </table>
      }
    </div>
  );
}

useGetCityQuery フックには、[get]クリック時の都道府県コードを渡す必要があるので、

  • inputCode:入力中の都道府県コード
  • code:[get]クリック時に inputCode を設定

としています。
code が空のときはクエリを発行したくないので、フックに skip: True を渡して発行を止めています。
データは現状の値が返されるので、cities は undefined にしています。

REST API の実行中は ‘Loading…’ の表示にしてみましたが、REST API が軽くて目視では確認できませんでした。
また、同じクエリは RTK Query がキャッシュから返してくれる為、同じ都道府県コードのときは REST API は発行されません。

そして、store に作成した Reducer を既存の store に加えました。

import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import { cityApi } from './services/city';

export const store = configureStore({
  reducer: {
    counter: counterReducer,
    [cityApi.reducerPath]: cityApi.reducer,
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware().concat(cityApi.middleware),
});

export type AppDispatch = typeof store.dispatch;
export type RootState = ReturnType<typeof store.getState>;
export type AppThunk<ReturnType = void> = ThunkAction<
  ReturnType,
  RootState,
  unknown,
  Action<string>
>;

前回のコードよりもスッキリしましたね。ファイルの構成も次の通りになりました。

createAsyncThunk と RTK Query 、どちらも使い易くできていますね。

コメント