ReduxToolkitでの非同期通信を学習するにあたり、こちらの記事に出会いました。
【React】ReduxToolkitで非同期処理を実装する
Qiitaの記事一覧を取得するという、とても親近感のわく題材でして、解説もとてもわかりやすくオススメの記事です。
createAsyncThunkを使って書き換えてみる
同じ題材で、createAsyncThunkを使ったら、どのようなコードになるのか気になったため、やってみます。
参考記事のサンプルコードをベースにして、Sliceのコードを以下のように書き換えてみます。
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
import { getItems } from '../api/qiitaApi';
// Thunk
export const fetchItems = createAsyncThunk(
'fetchItemsThunk',
async () => getItems()
)
// Slice
export const qiitaSlice = createSlice({
name: 'qiita',
// stateの初期値を設定
initialState: { loading: false, error: null, items: [] },
reducers: {},
extraReducers: builder => {
// 通信を開始した時
builder.addCase(fetchItems.pending, (state, action) => {
state.loading = true;
state.error = null;
})
// 通信が失敗した時
builder.addCase(fetchItems.rejected, (state, action) => {
state.loading = false;
state.error = action.error.stack;
})
// 通信が成功した時
builder.addCase(fetchItems.fulfilled, (state, action) => {
state.loading = false;
state.error = null;
state.items = action.payload;
})
},
});
// Selectors
export const selectQiita = ({ qiita }) => qiita;
// Reducer(must be default export)
export default qiitaSlice.reducer;
これで、コードの変更は終了です。
以降は解説です。
fetchItemsの変更点
変更前のコードは、Sliceで定義したActionを fetchItems
の中でdispatchしています。
変更後のコードは、Sliceで定義したActionは使わず、 createAsyncThunk
で包むだけにしました。
変更前
// Actions
export const { fetchStart, fetchFailure, fetchSuccess } = qiitaSlice.actions;
// 外部からはこの関数を呼んでもらう
export const fetchItems = () => async dispatch => {
try {
dispatch(fetchStart());
dispatch(fetchSuccess(await getItems()));
} catch (error) {
dispatch(fetchFailure(error.stack));
}
};
変更後
// 外部からはこの関数を呼んでもらう
export const fetchItems = createAsyncThunk(
'fetchItemsThunk',
async () => getItems()
)
Recucerの変更点
createAsyncThunk
は以下の3つのActionを生成してくれるので、それを利用しています。
-
pending
: 今回はAPI通信開始時に相当 -
rejected
: 今回はAPI通信失敗時に相当 -
fulfilled
: 今回はAPI通信成功時に相当
これらのActionは、 extraReducers
の builder
を利用して追加しています。
変更前
reducers: {
// 通信を開始した時に呼ぶ関数
fetchStart(state, action) { /* 中略 */ },
// 通信が失敗した時に呼ぶ関数
fetchFailure(state, action) { /* 中略 */ },
// 通信が成功した時に呼ぶ関数
fetchSuccess(state, action) { /* 中略 */ },
},
変更後
extraReducers: builder => {
// 通信を開始した時
builder.addCase(fetchItems.pending, (state, action) => { /* 中略 */ })
// 通信が失敗した時
builder.addCase(fetchItems.rejected, (state, action) => { /* 中略 */ })
// 通信が成功した時
builder.addCase(fetchItems.fulfilled, (state, action) => { /* 中略 */ })
},
rejected
の時、 action.payload
は undefined
ですので、ご注意ください。
代わりに、 action.error
が利用できます。
さいごに
本記事作成にあたり、以下の記事を参考にさせていただきました。ありがとうございました。