React Hooks + Redux ToolKit  (+ TypeScript + axios)でQiitaAPIをたたく（非同期処理）

# 目的
React Hooks + Redux ToolKit (+ TypeScript + axios)を使った非同期処理の実装を行います。
今回はQiitaのユーザーの記事一覧取得APIを対象にします。
https://qiita.com/api/v2/docs
`GET /api/v2/users/:user_id/items`

また、Redux Toolkitの詳しい説明はこちらの記事で紹介されているので是非参考にしてみてください。

https://www.hypertextcandy.com/learn-react-redux-with-hooks-and-redux-starter-kit

参照：[Redux ToolKit 公式サイト](https://redux-toolkit.js.org/)

先に動作イメージを確認する方は[こちら](#アプリ実行)

# 環境構築
今回はRedux + TypeScript のテンプレートを使用します。

```terminal
npx create-react-app my-app --template redux-typescript
```

作成したアプリにaxiosを追加します。

```
axiosをインストール
npm install axios@0.21.1
```

フォルダ構造は以下に示します（今回使用するフォルダのみ記載しています）。

```
my-app
 └ src
    ├ App.tsx
    ├ app
    │  └ store.js
    └ features
       └ qiita
          │ Qiita.tsx
          └ qiitaSlice.js
```

# 実装
実装手順として、qiitaSliceに非同期処理を実装し、そのreducerをstoreに登録します。
その後、storeに登録されているqiitaSliceの状態をQiitaコンポーネントで参照を行う動きになります。

## Sliceの実装

```typescript:qiitaSlice.js
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import axios from "axios";
import { RootState } from "../../app/store";

interface Item {
  rendered_body: string;
  body: string;
  coediting: boolean;
  comments_count: number;
  created_at: string;
  group: any;
  id: string;
  likes_count: number;
  private: boolean;
  reactions_count: number;
  tags: {
    name: string;
    versions: [];
  }[];
  title: string;
  updated_at: string;
  url: string;
  user: any;
  page_views_count: null | number;
  team_membership: any;
}

interface Items {
  items: Item[];
}

const initialState: Items = {
  items: [],
};

/** データ取得非同期処理 */
// 投稿ーデータを取得
export const fetchAsyncGetUserItems = createAsyncThunk(
  "qiita/getUserItems",
  async (userName: string) => {
    const { data } = await axios.get<Item[]>(
      `https://qiita.com/api/v2/users/${userName}/items`
    );
    return data;
  }
);

/** Slice */
const qiitaSlice = createSlice({
  name: "qiita",
  initialState: initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchAsyncGetUserItems.fulfilled, (state, action) => {
      return {
        ...state,
        items: action.payload,
      };
    });
    builder.addCase(fetchAsyncGetUserItems.rejected, (state, action) => {
      return {
        ...state,
        items: initialState.items,
      };
    });
  },
});

// 各コンポーネントからstateを参照できるようにエクスポートをしておく
export const selectItems = (state: RootState) => state.qiita.items;

export default qiitaSlice.reducer;

```

今回の肝となるSliceの実装になります。１つずつ見ていきましょう。

- まず、前半のinterface定義はAPIの戻り値の型になります。

- 次に非同期処理部分ですが、これはSliceの生成とは別に関数として定義します。

```typescript
export const fetchAsyncGetUserItems = createAsyncThunk(
  "qiita/getUserItems",
  async (userName: string) => {
    const { data } = await axios.get<Item[]>(
      `https://qiita.com/api/v2/users/${userName}/items`
    );
    return data;
  }
);
```
この時使用されるのがredux-toolkitで用視されている非同期要求関数`createAsyncThunk`になります。
第一引数に任意の非同期処理名、第二引数に非同期ロジックのコールバック関数を渡します。
今回はQiitaのユーザーの記事一覧取得APIを対象にしていますが、ユーザー名だけ引数として受け取るようにしています。

- そしてSliceを作成している部分が以下になります。
ここではSliceの名前、保持するstateの初期値、Stateに対して許可する更新処理を定義していきます。

```react
const qiitaSlice = createSlice({
  name: "qiita",
  initialState: initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchAsyncGetUserItems.fulfilled, (state, action) => {
      return {
        ...state,
        items: action.payload,
      };
    });
    builder.addCase(fetchAsyncGetUserItems.rejected, (state, action) => {
      return {
        ...state,
        items: initialState.items,
      };
    });
  },
});
```

こちらの`extraReducers`に非同期処理のパターンを`addCase`で追加します。
ちなみに定義できるのは以下の3種類となります。
- pending: 保留中
- fulfilled: 成功
- rejected: 失敗

今回はAPIリクエストが成功した場合は戻り値でstateを更新するコールバック関数を、
失敗した場合は初期値でstateを更新するコールバック関数を定義しています。

最後にstoreに登録するreducerと、登録後に各コンポーネントからstateの値を参照するための関数をエクスポートしておきます。

```react
// 各コンポーネントからstateを参照できるようにエクスポートをしておく
export const selectItems = (state: RootState) => state.qiita.items;

export default qiitaSlice.reducer;
```
この`state.qiita.items`の`qiita`にあたる部分はこの後実装するstoreへの登録名になります。

以上でSliceの実装は完了です。
ここまでくればあと少しです！！


## storeの実装
上記の createSlice で作成した各 Reducer を一つにまとめ、ストアを生成します。
[環境構築](#環境構築)の手順通りにアプリを作成した場合、storeのテンプレートはすでに作成済みかと思います。
今回追加した部分にはコメントを載せています。

```react:store.ts
import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit";
import qiitaReducer from "../features/qiita/qiitaSlice"; // 追加

export const store = configureStore({
  reducer: {
    qiita: qiitaReducer, // 追加
  },
});

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

```
以上でstoreの実装は完了です。

## コンポーネントの実装
最後にSliceで管理されているstateの参照や非同期処理の実行を定義します。

```react:Qiita.tsx
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchAsyncGetUserItems, selectItems } from "./qiitaSlice";

const Qiita: React.FC = () => {
  const itemsData = useSelector(selectItems);
  const dispatch = useDispatch();

  useEffect(() => {
    // 非同期処理を実行 // 引数に例として私のQiitaアカウント名を指定
    dispatch(fetchAsyncGetUserItems("eiji-noguchi"));
  }, [dispatch]);

  const items = itemsData.map((item) => (
    <div key={item.id}>
      <p>{item.title}</p>
      <a href={item.url}>{item.url}</a>
    </div>
  ));

  return <>{items}</>;
};

export default Qiita;
```

コンポーネント内からは`useSelector`をつかってstateの値を参照しています。
また、`useDispatch`を使い非同期処理を実行しています。ここであ`useEffect`と合わせて、最初の表示時に非同期処理を呼ぶようにしています。

ついでに取得したユーザーの記事のタイトル一覧を画面に表示してみましょう。
作成したQiitaコンポーネントを`App.tsx`に埋め込みます。

```react:App.tsx
import "./App.css";
import Qiita from "./features/qiita/Qiita";

function App() {
  return (
    <div className="App">
      <Qiita />
    </div>
  );
}

export default App;

```

以上で実装は完了になります。

## アプリ実行

```
npm start
```

で実行してみましょう。こんな画面が出ればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621838/a66eba76-ec65-4b57-d36b-cdd4868c9b62.png)


## まとめ
redux-toolkitを使った非同期処理を実装できるようになりました。
複雑になりがちな状態管理を定まったルール内で使えるようになるredux-toolkitはこれからのReact開発に必須なライブラリになると思われます。
正直、最初からすべてを理解するのは大変ですが、慣れるとReact開発において強力な一手になること間違いなしですね。