【Redux】カウントアプリを作りながらReduxを理解しよう！


# 概要
Reduxは状態管理のライブラリです。Reactで状態管理をする方法は色々あります。最近だとHooksのuseContextとかあるけど、まだReduxが使われるケースも多いでしょうってことで、Reduxの使い方についてまとめてみました！最初にReduxの概念の説明して、後半は実際にカウントアプリを作りながらReduxの説明をしていきます

### Reduxとは？
Reactではコンポーネントに状態を待たしていましたが、Reduxを使うことでstoreでstateを管理することができます。Reactではstateは子のコンポーネントにしかpropsで渡すことができませんでしたが、下図のようにReduxではstoreを使うことで親のコンポーネントにバケツリレーさせずにstateを渡すことができます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927983/5d33230c-96a2-c650-5376-1573c4a7c297.png)

[画像引用元](https://css-tricks.com/learning-react-redux/)

### どんな時に使うのか
- 大規模なアプリ向けです、大規模なアプリで状態管理を使わないとトップのコンポーネントにstateが集まってしまったり、propsが多くなって扱いづらくなってしまいます。

### 処理の流れ
- **Action**  : stateを切り替えたい時のアクションを定義してあります
- **Reducer** : actionに応じてstateを変更する処理が書いてあります
- **Store**   : stateの情報を保持します

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927983/d08caa68-7e4a-cf23-6648-98b14ffa17fe.png" width="700px">
[画像引用元](https://itnext.io/integrating-semantic-ui-modal-with-redux-4df36abb755c)

上記のように、**Action→Reducer→Store→UI**に反映の順で処理が進みます

以降は、言葉で説明するよりも実際に作る方が分かりやすいと思うので**カウントアプリを作りながらReduxの使い方を説明していきます**


# カウントアプリデモ画面
![ezgif.com-gif-maker (9).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927983/ddd3aef2-6fc7-fdf6-27c6-0340e9c8bf04.gif)

# 作成手順
## 1. 環境構築
### Create-React-App

```
$ npx create-react-app アプリ名
```

### インストール
reduxを使うためのライブラリをインストールしていきます

```
# NPM
$ npm install redux react-redux
# YARN
$ yarn add redux react-redux
```

**redux**:redux本体
**react-redux**:reactでreduxを使うためのライブラリ

**確認方法**
・ package.jsonのdependenciesで確認できます

※ 今回使用しません
`@reduxjs/toolkit`
`redux-devtools`


## 2. アプリ作成
### ディレクトリ構成を編集
srcディレクトリを下記のように変更しましょう。

**ディレクトリ構成**

```
src
├── App.js
├── actions
├── index.js
├── reducers
└── store
```

srcディレクトリ内の今回必要ないファイルを削除して、**actions**と**reducers**と**store**ディレクトリを追加してあります

### Action作成
actionsディレクトリにindex.jsファイルを作成して以下のように書き換えます

```actions/index.js
export const increment10 = () => {
  return {
    type: 'INCREMENT10',
  };
};
export const increment1 = () => {
  return {
    type: 'INCREMENT1',
  };
};
export const decrement1 = () => {
  return {
    type: 'DECREMENT1',
  };
};
export const decrement10 = () => {
  return {
    type: 'DECREMENT10',
  };
};

```

上記のように、actionはtypeとゆうプロパティを持っているオブジェクトを返している関数です。次に説明するReducerでtype事に処理を分岐させていきます

### Reducer作成
reducersディレクトリにcounter.jsを作成して以下のように書き換えます
#### counter.js

```reducers/counter.js
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case 'INCREMENT10':
      return state + 10;
    case 'INCREMENT1':
      return state + 1;
    case 'DECREMENT1':
      return state - 1;
    case 'DECREMENT10':
      return state - 10;
    default:
      return state;
  }
};

export default counterReducer;

```
stateとactionを受け取って、actionのtype事に条件分岐して加算、減算した新しいstateをreturnしています。仮パラメータのstateに0が代入されていますね。これは、stateの初期値を表しています


次に、reducersディレクトリにindex.jsを作成して以下のように書き換えてください
#### index.js

```reducers/index.js
import counterReducer from './counter';
import loggedReducer from './isLogged';
import { combineReducers } from 'redux';

const allReducers = combineReducers({
  counter: counterReducer,
});

export default allReducers;
```
Reducersをcombine(組み合わせ)しています。
今回、Reducerは1つしか作成していないので`combineReducers`は使わなくても作成できますが、便利なので紹介しときました

### Storeを作成

```store/index.js
import { createStore } from 'redux';
import allReducers from '../reducers';

const store = createStore(
  allReducers,
  window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);

export default store;

```
`window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);` これは、[Redux DevTools Extension](https://github.com/zalmoxisus/redux-devtools-extension)を使う時に必要なコードです。とても便利なChrome DevToolsの拡張機能なので最後に紹介します

### src/index.jsx
```src/index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { Provider } from 'react-redux';
import store from './store';

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

```
Providerで<App />を囲みます。これを書くことでstoreのstateをコンポーネントに渡すことができます、Providerの`store={store}`はstore属性に今回作成したstoreを指定して対応させてます

### src/App.jsx
それでは実際にApp.jsxに対応させてアプリを完成させます

```src/App.jsx
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment10, increment1, decrement1, decrement10 } from './actions';

const App = () => {
  const counter = useSelector((state) => state.counter);
  const dispatch = useDispatch();
  return (
    <>
      <h1>Counter: {counter}</h1>
      <button onClick={() => dispatch(increment10())}>+10</button>
      <button onClick={() => dispatch(increment1())}>+1</button>
      <button onClick={() => dispatch(decrement1())}>-1</button>
      <button onClick={() => dispatch(decrement10())}>-10</button>
    </>
  );
};

export default App;
```
`import { useSelector, useDispatch } from 'react-redux';`　のuseSelectorとuseDispatchはRedux Hooksと呼ばれるものです。 useSelectorはstoreのstateを取得するためのもの。useDispatchはReducerを呼び出しています。
※ `connect()`を使って書いてあるものがあったら古い書き方です
※ `dispatch(increment10())`は`dispatch({type: 'INCREMENT10'})`と同じ意味なので書き換えてもらっても構いません。私的にはアクション名の方が分かりやすいです

**これでカウントアプリは完成です。**最後にRedux DevTools Extensionだけ紹介します！

### Redux DevTools Extension
まずはRedux DevTools Extensionのインストールが終わってない場合は[Redux DevTools Extension](https://github.com/zalmoxisus/redux-devtools-extension)のREADME.mdのInstallationにそってインストールしてください。
インストールが終わったらChrome DevToolsを開いてReduxのタブを開いてアプリを実行させstoreのstateを変更してみます

![ezgif.com-gif-maker (12).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/927983/7286770d-930a-a478-6935-ede454931327.gif)

上記のように、左側でアクションのtypeプロパティ、右側でstoreのstateの差分が表示されました。便利ですよね、他にも色々な機能があるのでREADME.mdを読んでみてください


# おわりに
Reduxは少し分かりにくいですよね、何か詰まってるところがあれば今回の記事で解消されれば幸いです
参考: [redux公式](https://redux.js.org/)