useStateからreact-reduxに置き換える

## 前提
以前作成した下記記事のHooksを使って置き換えます

https://qiita.com/sh19982580/items/eaa599f8b8c74bb4d87e


## reduxとtoolkitの導入

`yarn add react-redux @types/react-redux @reduxjs/toolkit`

## ファイルの変更と追加 

index.tsx

    import React from "react";
    import ReactDOM from "react-dom";
    import { Provider } from "react-redux";

    import App from "./app";
    import { store } from "./redux/store";

    ReactDOM.render(
    <React.StrictMode>
        <Provider store={store}>
        <App />
        </Provider>
    </React.StrictMode>,
    document.getElementById("root")
    );


- `<Provider>`でAppを囲う

`<Provider>`：Reduxのstoreを利用できるようにする

src > reduxフォルダ > store.ts

    import { configureStore, combineReducers } from "@reduxjs/toolkit";

    export const store = configureStore({
    reducer: combineReducers({}),
    });

    export type RootState = ReturnType<typeof store.getState>;
    export type AppDispatch = typeof store.dispatch;

- store：`state`の保存場所
- `useState`では`props`でデータをバケツリレーしていたが、`redux`では`store`から直接データを渡せる
- 様々なサイトを見た時に`configureStore`と`combineReducers`のファイルを分けていることがあるが、最初は`store.ts`にまとめておく(Reducerが多くなったら切り分ける可能性あり)

## fetchの置き換え

app.tsx `useState版`

    import React, { FC, useEffect, useState } from "react";
    import { Content } from "./component/content";
    import { Ranking } from "./types/type";

    const App: FC = () => {
      const [ranking, setRanking] = useState<Ranking[]>([]);

      useEffect(() => {
        const fetchData = async () => {
          const response = await fetch("http://localhost:3000/ranking");
          const rankingData = await response.json();
          setRanking(rankingData);
        };
        fetchData();
      }, []);
      return (
        <>
          <Content ranking={ranking} />
        </>
      );
    };
    export default App;

app.tsx `redux置き換え版`

    import React, { FC, useEffect } from "react";
    import { useDispatch } from "react-redux";

    import { Content } from "./component/content";
    import { ranking } from "./redux/fetch";

    const App: FC = () => {
      const dispatch = useDispatch();
      useEffect(() => {
        dispatch(ranking());
      }, []);
      return (
        <>
          <Content />
        </>
      );
    };
    export default App;
- `useEffect`内にあった`fetch`を切り分け
- `useState`が必要なし
- `Content`コンポーネントに渡していたデータも必要なし

※`fetch`の切り分けは下記

redux > fetch.ts

    import { createAsyncThunk } from "@reduxjs/toolkit";

    export const ranking = createAsyncThunk("rankingData", async () => {
    const response = await fetch("http://localhost:3000/ranking");
    const rankingData = await response.json();
    return rankingData;
    });

- fetchの際に`async`を使っていたので非同期処理であり、[Reduxのデータフェッチ](https://redux-toolkit.js.org/usage/usage-guide#redux-data-fetching-patterns)とRedux toolkitに記載されていたので使用

`createAsyncThunk`：非同期処理に対応したActionCreatorを生成する関数

## ここまでで確認①

1. `app.tsx`の`<Content>`は一旦、`<p>TEST</p>`に変更
1. `content.tsx`のコードは全てコメントアウト
1. `yarn mock`と`yarn start`を実行
1. `http://localhost:8111`でブラウザの検証からReduxを開く※google chromの拡張機能[Redux DevTools](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=ja)
 
`rankingData/fullfilled`の`action > payload`内にデータが入っていればdispatchしているfetchは成功

## Sliceを使ってStoreにデータを送る
redux > 　rankingSlice.ts

    import { createSlice } from "@reduxjs/toolkit";

    import { RankingData } from "../types/type";
    import { ranking } from "./fetch";

    const initialState: RankingData = {
    ranking: [],
    };

    export const rankingDataSlice = createSlice({
    name: "rankingData",
    initialState,
    reducers: {},
    extraReducers: (builder) => {
        builder
        .addCase(ranking.pending, () => {
            //非同期処理中のロジック
        })
        .addCase(ranking.fulfilled, (state, { payload }) => {
            //非同期処理成功時のロジック
            state.ranking = payload;
        })
        .addCase(ranking.rejected, (error) => {
            //非同期処理失敗時のロジック
            error;
        });
     },
    });

- sliceでデータの更新
- 今回のuseStateの役割 = Slice

1. `useState版`：`ranking` = `redux版`：`initialState`
1. `useState版`：`setRanking` = `redux版`：`extraReducers`

`extraReducers`：「外部」アクションを参照することを目的としているため、アクションで生成されたものは使えない
`reducers`：特定のアクションタイプを処理するための関数で、switchのcase文に相当

store.ts

    import { rankingDataSlice } from "./rankingSlice";
    import { configureStore, combineReducers } from "@reduxjs/toolkit";

    export const store = configureStore({
    reducer: combineReducers({ ranking: rankingDataSlice.reducer }),
    });

    export type RootState = ReturnType<typeof store.getState>;
    export type AppDispatch = typeof store.dispatch;


- storeでデータ保管
- `rankingDataSlice`をimport
- `reducer`にSliceのデータを追加

## ここまでで確認②
1. `yarn mock`と`yarn start`を実行
1. ブラウザの検証からReduxを開く

`rankingData/fullfilled`の`state > ranking > ranking`内にデータが入っていればstoreにデータが入っているのでOK！
あとはstoreからデータを取り出すだけ!

## Storeからデータを与える
今回Storeからデータを与えたいのは子である`content.tsx`

content.tsx

    import React, { FC } from "react";
    import { useSelector } from "react-redux";
    import { RootState } from "../redux/store";
    import { RankingData } from "../types/type";

    export const Content: FC<RankingData> = () => {
      const { ranking } = useSelector((state: RootState) => state.ranking);
      if (!ranking) throw new Error("rankingがありません");

      return (
        <>
          <h1>Hello Hello</h1>
          <table>
            <tr>
              {ranking.map(({ word }) => {
                return (
                  <tr>
                    <td key={word}>{word}</td>
                  </tr>
                );
              })}
            </tr>
          </table>
        </>
      );
    };


1. コメントアウトを消す
1. `props`の部分も消す
1. `app.tsx`の`<p>TEST</p>`は`<Content/>`に変更
1. `<Content/>`でエラーが出ているので`type.d.ts`を編集(rankingに?をつける)

        export interface RankingData {
          ranking?: Ranking[];
         }

- `redux`から`useSelector`をimport
- `state`を管理している`store`から`RootState`もimport
- あとは`useSelector`で`ranking`のデータを持ってくる
- `ranking`を持ってきた時に`undefined`の可能性があるのでタイプガードをする

## 最後に

`yarn mock`と`yarn start`を実行
ここでmockのデータが表示されていたら成功！

今回はcomponent1つですが複数になった場合や孫のコンポーネントが出てきた場合、useStateであるとpropsのバケツリレーは大変です。
理解するのは難しいですがreduxとtoolkitを使えるようになるとコードがスッキリし、`store`から直接データを渡せるので便利です！

## 参考文献

https://react-redux.js.org/introduction/getting-started

https://redux-toolkit.js.org/introduction/getting-started

https://zenn.dev/jojo/articles/25c10b27783093

https://zenn.dev/luvmini511/articles/819d8c7fa13101#3-1.-aciotn-%2B-reducer-%E2%86%92-slice

https://www.webopixel.net/javascript/1601.html

https://zenn.dev/luvmini511/articles/c9cdb77a145f4d