ReduxとReduxToolkitを使用してReact内でデータを管理する

# はじめに
[前回のReactを使用してWeb画面を作成する](https://qiita.com/mink0212/items/a53b285dd26f57b835b3)では、Reactの簡単なサンプルと共に説明をまとめました。次はデータを持ちまわすためにReduxとReduxToolkitの簡単なサンプルと説明をまとめようと思います。
最低限の簡単なサンプルなのでアクションは別ファイルにするべきであったり、といったベストプラクティス的なものは他のサイトで調べてください。

# 環境
 - node.js: v12.18.2
 - webpack: 4.44.1
 - React: 16.13.1
 - Redux: 7.2.1

# 環境作成
[前回のReactを使用してWeb画面を作成する](https://qiita.com/mink0212/items/a53b285dd26f57b835b3)の続きとなります。
環境構築などはそちらを見てください。

## Redux
Reactのコンポーネント間でデータを共有するためにReduxというライブラリを使用します。

### Reduxのインストール
ReduxのライブラリとReduxをシンプルに記載するためのtoolkitをインストールします。

```

npm install react-redux
npm install @reduxjs/toolkit
```

# シンプルなRedux
## Reduxのソースの作成

### フォルダの作成
Reduxのソースをstore、slice、コンポーネントに分けて作成するので、コンポーネント以外のフォルダを作成してください。コンポーネントは前回作成したsrcの下に入れます。

```

project_root
├─src    // reactのJavaScriptファイルやCSSファイルを格納
├─store  // redux toolkitのstore(reduxのstoreをまとめたもの)ファイルを格納
  ├─slice    // redux toolkitのslice(reduxのactionとreducerをまとめたもの)
```

### sliceファイルの作成
内部的に保持する情報と処理をまとめたものをsliceファイルとして作成しています。
今回は単純にWeb画面と文字のやり取りをするため保持するデータは``` "mess" ```、処理は"hello"をデータに置き換える処理としています。

``` messageSlice.js

import { createSlice } from '@reduxjs/toolkit';
import axios from 'axios';

export const messageSlice = createSlice({
  // slice名
  name: 'message',
  // 内部で保持するデータ(キー:mess, 初期値:メッセージ)
  initialState: {
      "mess": "メッセージ"
  },
  // 内部のデータにアクセスするための処理(処理名:sayhello)
  reducers: {
    sayhello: state => {
      state.mess = "hello";
    }
  },
});

// 外からインポートするためにactionとreducerをエクスポートする
export const { sayhello } = messageSlice.actions;
export default messageSlice.reducer;

```

### storeファイルの作成
先ほど作成したsliceのreducerをstoreに登録することで各コンポーネントで情報を共有できるようにします。

``` store.js

import { configureStore } from '@reduxjs/toolkit';
import messageReducer from './slice/messageSlice';

export default configureStore({
  reducer: {
    message: messageReducer,
  },
});
```

### storeをコンポーネントに適用させる
コンポーネント間で情報をやり取りするために先ほど作成したstoreをreactのrenderに登録します。

``` index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

// redux用のインポート
import { Provider } from 'react-redux'
import store from '../store/store'

ReactDOM.render(
  // インポートしたstoreを登録する
  <Provider store={store}>
    <App />,
  </Provider>,
  document.getElementById('app')
);
```

### slice経由でstoreを使用する
コンポーネントで情報を処理するためにsliceのaction経由でstoreを操作します。

``` App.js

import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { sayhello } from '../store/slice/messageSlice';

export function Message() {
  // store内の値を取得
  const message = useSelector(state => state.message.mess);
  // actionを操作するための関数取得
  const dispatch = useDispatch();

  return (
    <div>
      <div>
        {/* Sliceで定義したactionをdispatch経由で呼び出す */}
        <button aria-label="hello" onClick={() => dispatch(sayhello())}>
          こんにちは
        </button>
        {/* 上で呼び出したmessageを表示する */}
        <span>{message}</span>
      </div>
    </div>
  );
}
```

### 最終的なフォルダ構成

```

project_root
├─dict
├─public
|  Ⅼ-index.html
├─src    
|  ├─App.js
|  Ⅼ─index.js
├─store  
|  ├─slice  
|  | Ⅼ─messageSlice.js
|  Ⅼ-store.js
├─.babelrc
├─package.json
Ⅼ─webpack.config.js
```

### Reactの実行
前回同様にReactを開発用のサーバで起動してブラウザからアクセスしてください。

```

"./node_modules/.bin/webpack-dev-server"
```

表示されたWeb画面にこんにちはというボタンがあると思うため、それをクリックすると隣の文字がhelloに変わります。
内部の処理としては、ザックリ言うと以下のようなイメージになります。
画面描画時に``` App.js ```内で```useSelector```を使用することにより、``` messageSlice.js ```で定義した``` mess ```変数を呼び出しています。
その呼び出した変数を```<span>{message}</span>```と紐づけて変数が変わったら自動的に変わるように使用しています。
ボタンを押したら``` messageSlice.js ```で定義した``` sayhello ```を呼び出して``` mess ```変数を更新して、再描画しています。


# 画面から受け取るRedux
先ほどはactionの中で定義した値に更新していましたが、今度はWeb画面に入力した内容を使用してstoreを更新します。

## Reduxのソースの作成
先ほどのファイルに処理を追加して機能を実装します。

### sliceファイルの作成

``` messageSlice.js

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

export const messageSlice = createSlice({
  name: 'message',
  ～～～　上と同じ　～～～

  reducers: {
  ～～～　上と同じ　～～～
  // この処理を追加します。
    sayAmount: (state, action) => {
      state.mess = action.payload;
    },
  },
});

// 追加したsayAmountをエクスポートできるようにする
export const { sayhello, sayAmount} = messageSlice.actions;

export default messageSlice.reducer;
```

### storeファイルの作成
storeファイルは、上のreducerをまとめて登録しているので変更なしです。

### storeをコンポーネントに適用させる
storeファイルは、上のreducerをまとめて登録しているので変更なしです。

### slice経由でstoreを使用する
内容としては。ほとんど先ほどのものと変わらないです。
8行目のところで``` useState ```を使用して2つの関数を生成していますが、ザックリ言うとクラス内のstateの宣言などを不要にする物になります。

``` Message.js

import React, { useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { sayhello, sayAmount } from '../store/slice/messageSlice';

export function Message() {
  const message = useSelector(state => state.message.mess);
  const dispatch = useDispatch();
  const [messsageAmount, setMesssageAmount] = useState('2');
  ～～～　上と同じ　～～～
        {/* テキストボックスとボタンにインポートしたものを適用する  */}
        <input aria-label="set amount" value={messsageAmount} onChange={e => setMesssageAmount(e.target.value)} />
        <button onClick={() => dispatch(sayAmount(messsageAmount))}>
          テキスト変更
        </button>
      </div>
    </div>
  );
}
```

### Reactの実行
上と同様にReactを開発用のサーバで起動してブラウザからアクセスしてください。
今回追加したテキストボックスとボタンが追加されています。テキストボックスに値を入れてボタンをクリックすると表示されているテキストが変更されます。
基本的に内容としてはほぼ表示のみと変わりません。


# 終わりに
axiosとの連携を書こうと考えていましたが、Reduxの説明が予想以上に長くなったので今回はここまでにします。~~次回以降にaxiosのサンプルと簡単な説明を書いていこうと思います。~~axiosのサンプルと説明は[ReactのRedux内でaxiosを使用した通信をする](https://qiita.com/mink0212/items/b89388f9c255d7ef6833)にまとめました。
今回は簡単な例なのでメリットがわからないと思いますが、Web画面や保持する情報が増えたら明確にメリットがわかるようになると思います。