【React】ReduxToolkitのcreateAsyncThunkで非同期処理を実装する

ReduxToolkitでの非同期通信を学習するにあたり、こちらの記事に出会いました。

[【React】ReduxToolkitで非同期処理を実装する](https://qiita.com/ozaki25/items/1375336826a021370528)

Qiitaの記事一覧を取得するという、とても親近感のわく題材でして、解説もとてもわかりやすくオススメの記事です。

# createAsyncThunkを使って書き換えてみる

同じ題材で、createAsyncThunkを使ったら、どのようなコードになるのか気になったため、やってみます。

参考記事のサンプルコードをベースにして、Sliceのコードを以下のように書き換えてみます。

```src/store/qiitaSlice.js
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` で包むだけにしました。

#### 変更前

```javascript
// 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));
  }
};
```

#### 変更後

```javascript
// 外部からはこの関数を呼んでもらう
export const fetchItems = createAsyncThunk(
    'fetchItemsThunk',
    async () => getItems()
)
```


## Recucerの変更点

`createAsyncThunk` は以下の3つのActionを生成してくれるので、それを利用しています。

- `pending` : 今回はAPI通信開始時に相当
- `rejected` : 今回はAPI通信失敗時に相当
- `fulfilled` : 今回はAPI通信成功時に相当

これらのActionは、 `extraReducers` の `builder` を利用して追加しています。

#### 変更前

```javascript
  reducers: {
    // 通信を開始した時に呼ぶ関数
    fetchStart(state, action) { /* 中略 */ },
    // 通信が失敗した時に呼ぶ関数
    fetchFailure(state, action) { /* 中略 */ },
    // 通信が成功した時に呼ぶ関数
    fetchSuccess(state, action) { /* 中略 */ },
  },
```

#### 変更後

```javascript
  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` が利用できます。


# さいごに

本記事作成にあたり、以下の記事を参考にさせていただきました。ありがとうございました。

- [【React】ReduxToolkitで非同期処理を実装する](https://qiita.com/ozaki25/items/1375336826a021370528)
- [React Reduxには今後Redux Toolkitも使うのがいいと思う](https://qiita.com/shikazuki/items/02fb27dc741cbff18811)