前回は 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 、どちらも使い易くできていますね。
コメント