Redux初心者のまとめ（1）

Redux初心者の自身の経験から、覚えたことをまとめてみたいと思います。Reactは触ったことがあって、これからReduxやってみようかなという方は参考にしていただけるのではないかと思います。

その前にRedux入門のイントロダクション記事を書いていますので、先にこちらも参照してみてください。
[Redux入門（イントロダクション）](https://qiita.com/tomi_shinwatec/items/edb769bdfc80bc6c0f28)

また、さらにその前にReactのまとめ記事もありますので、こちらも参考にしてください。
（Reactの概念を理解している前提でReduxの解説をしていますので、React自体初めての方はまずこちらを読まれることをオススメします）
[React初心者のまとめ（基本編：1/3）](https://qiita.com/tomi_shinwatec/items/6563eacd88e63bda5de3)
[React初心者のまとめ（基本編：2/3）](https://qiita.com/tomi_shinwatec/items/cdd319d8fce8bdb829d0)
[React初心者のまとめ（基本編：3/3）](https://qiita.com/tomi_shinwatec/items/975eceda3d07b152547c)


ということでReduxの解説に入りたいと思います。
実用よりも解説優先のため、本来はReactのみでも書けるような単純な機能のコードで解説を進めていきます。

## 全体の流れ
全体の流れは以下になります。React単体でも流れそのものは似ています。この部分については前述のイントロダクション記事でReact単体と比較していますので参考にしてみてください。

Component
：ボタンクリックなどトリガー動作によって、内部でActionを実行
　　↓
Action / Action Creator
：外部問い合わせなどを行い、情報を集めてStoreに設定する新たな値を用意する
　　↓
Reducer
：Storeの値を書き換える
　　↓
Storeの変更
：Reducerの指示によって変更された値を格納
　　↓
Component(render)
：storeの内容に応じて表示書き換え

## インストール
まず前準備で"redux"と、Reactとreduxを繋げるための"react-redux"をnpmで入れます。

```
$ npm i -D redux react-redux
```

## コード
流れが分かりやすいよう、順にステップを追っていく形で完成まで記述していきます。
ここでは追加ボタンを押す度にカウントが増えていくというテストコンポーネントを作ります。

テストでの挙動確認は下記ツールの利用をオススメします。
環境構築を飛ばしていきなり動作テストが出来ますので、ちょっとした動きの確認にはとても便利です。
[CodeSandbox](https://codesandbox.io/)
※React用の環境を作るには右上の[Create Sandbox]ボタンを押して[React]を選択してください。

### コンポーネント
まずはコンポーネントの設置から。

```index-phase1.jsx
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";

class App extends React.Component{
  
  // ボタンをクリックしたときのアクション
  stateAddAction(){
    // MEMO: add(1); みたいな追加の処理を書く
  }
  render(){
    return(
      <div>
        <h1>テストコンポーネント</h1>
        <p>カウント：{/* MEMO:表示する値の変数を書く */}</p>
        <button type="button" onClick={()=>this.stateAddAction()}>追加する</button>
      </div>
    )
  }
}

const rootElement = document.getElementById("root");
// Rendering
ReactDOM.render(
  <Provider>
    <App />
  </Provider>,
  rootElement
);
```

枠組みの構成を先に行い、細かいところはMEMOと記述して、あとで追記していく形にしています。
ここで早速"\<Provider>"というものが追加になっています。詳しくは後述しますが、コンポーネントにStoreを渡すための記述になります。
あとで少し変更もありますので、ここでは記述ルールくらいに捉えておいてください。

状態管理を行うStoreをコンポーネントに接続するには、以下の記述を追記します。

```index-phase2.jsx
import React from "react";
import ReactDOM from "react-dom";
import { Provider,connect } from "react-redux"; // ←connect追加
import { addAction } from './action.jsx' // ←追加（※後述のaction）

class App extends React.Component{
 // ...index-phase1.jsxと同じのため省略...
}

// 変数の管理をする関数
const mapStateToProps = store => ({
  countValue: store.countValue
});

// 関数の管理をする関数
const mapDispatchToProps = dispatch => {
  return {
    add: value => dispatch(addAction(value))
  };
};

// connectで結合してAppコンポーネントに接続
const AppContainer = connect(
  mapStateToProps, // 1つ目のパラメータは変数
  mapDispatchToProps // 2つ目のパラメータは関数
)(App);

const rootElement = document.getElementById("root");
// Rendering
// <App /> → <AppContainer /> connectで生成したコンテナに変更
ReactDOM.render(
  <Provider>
    <AppContainer />
  </Provider>,
  rootElement
);
```
**import…**
冒頭のimport { addAction } …　でアクションを読み込んでいますが、これはまだ未作成のActionフローでの処理になります。（actionファイルを読み込んでいます。）とりあえずここでは読み込むことだけを理解しておいてください。
またconnectを新たに読み込んでいます。（動作は後述）

**mapStateToProps**
mapStateToPropsで取得したいStoreの値を記述します。
上記の例ではstore上のcountValueの値を取得して、コンポーネント内で使える変数countValueに紐付けています。コンポーネント内ではthis.props.countValueの形で利用できます。

**mapDispatchToProps**
mapDispatchToPropsではStoreを操作する関数を管理します。
mapStateToPropsと同じく、コンポーネント内で使えるadd関数とを紐付けていて、addを実行することでカンマの右記の関数を実行できます。
ここでdispatchという関数が出てきていますが、これはActionフローでの実行結果をreducerに渡すものです。この例ではActionフロー内のaddAction関数を実行して、結果をreducerに引き渡す処理をしています。

これによって、actionを実行した結果がreducerに渡り、reducerがStoreを変更する、という流れになります。

**connect**
connect関数ではAppコンポーネントにmapStateToPropsとmapDispatchToPropsを含めて、新たにAppContainerコンポーネントを作り出しています。ReactDOM.renderでその生成したコンテナを読み込む形に変更します。

### Action
ここで一旦アクションの記述に移ります。
コンポーネントで指定した、ボタンを押したときの「カウントが増える」というアクションはここで指定します。

```action-phase1.jsx
// Action Creators
export function addAction(value) {
  return {
    type: CHANGE,
    value : value //(プロパティと値の変数名が同じなので value のみでもOK)
  };
}
```

たったこれだけです。
actionと言いながら値の追加の処理もしないし、これをreturnして何が起こるのかという感じですが。。
前述のコンポーネント部分でdispatchを実行していますので、ここでStoreを変化させる「reducer」に処理を受け渡しています。
reducerに受け渡すには、「type」と「引き渡す値」を渡す必要があります。

**type**
reducerではこのtypeを見てどういう処理するかを決めます。
といってもあらかじめreducer側でtypeの数だけ動き方を決めておき、actionから渡されたtypeに応じて動作をスイッチする感じです。

**引き渡す値**
これは説明するまでもなくそのままなのですが、値を渡します。
例えば「HELLOを表示する」といった固定のactionならtypeのみでOKですが、「xxxを表示する」「数値を+nする」のような値が必要なパターンの動きも多々ありますので、その際のパラメータを渡します。これはtypeのように決まったプロパティでなくても自由な形で渡すことが出来ます。

### Reducer
Reducerは「現在の状態（state）」と「受け取ったAction」を引数に取り、新しい状態を返す関数です。
下記のようなソースになります。

```reducer.jsx
export default function reducer(state, action) {
  switch (action.type) {
    case "CHANGE":
      return Object.assign({}, state, {
        countValue: state.countValue + action.value
      });
    default:
      return state;
  }
}
```
actionにはActionフローから渡ってきた値そのものが格納されています。
そのためaction.typeは先ほどactionで記述したtypeの値になりますので、switchで動作を振り分けています。

ここでObject.assign()が使用されていますが、これは元のstateを書き換えないようにするためにクローンを作るためです。（reducerで直接stateを変更してはいけないというreduxのルールによるもの）
ソースの例ではObject.assign()でstateのクローンを作り、countValueの値だけを上書きして(action.valueの分だけ加算して)、その他の値はそのままにreturnしています。
この一連の処理によって、storeの値を変更することが出来ます。

### Store
Store自身がまだ未定義でした。
StoreはcreateStoreという関数で作ることが出来ます。

```store.jsx
import { createStore } from "redux";
import reducer from "./reducer.jsx";

const initialState = {
  countValue: 1
};

export const store = createStore(reducer , initialState);
```
createStoreでは、先ほど作ったreducerとStoreの初期値であるinitialStateを渡して生成します。

### コンポーネントの修正
ここまで来たら、もう一度コンポーネントに戻って修正します。

```index.jsx
import React from "react";
import ReactDOM from "react-dom";
import { Provider,connect } from "react-redux";
import { addAction } from './action.jsx'
import { store } from "./store.jsx"　// ←（追記）storeを読み込み

class App extends React.Component{
  
  // ボタンをクリックしたときのアクション
  stateAddAction(){
    this.props.add(1); //（追記）actionを実行する関数を記述
  }
  render(){
    return(
      <div>
        <h1>テストコンポーネント</h1>
        <p>カウント：{this.props.countValue}</p> {/*（追記）storeの値を表示 */}
        <button type="button" onClick={()=>this.stateAddAction()}>追加する</button>
      </div>
    )
  }
}

// 変数の管理をする関数
const mapStateToProps = store => ({
  countValue: store.countValue
});

// 関数の管理をする関数
const mapDispatchToProps = dispatch => {
  return {
    add: value => dispatch(addAction(value))
  };
};

// connectで結合してAppコンポーネントに接続
const AppContainer = connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

const rootElement = document.getElementById("root");
// Rendering
ReactDOM.render(
  {/*（追記）providerにstoreを渡す */}
  <Provider store={store}>
    <App />
  </Provider>,
  rootElement
);
```
それぞれの追記箇所を確認。

**storeを読み込み**
storeを読み込んでProviderにstoreを渡すことで、storeが使えるようになります。

**actionを実行する関数を記述**
this.props.add(1)のadd関数がmapDispatchToPropsで定義したaddになります。
なのでここでは1回クリックする毎に1増加するような動作を指定しています。

**storeの値を表示**
this.props.countValueがmapStateToPropsで定義したcountValueになります。
よってここではstoreのcountValue値を表示しています。

#### 挙動の確認
全体の流れを再確認します。

「追加する」ボタンがクリックされる
↓
stateAddActionを実行
↓
ActionフローのaddActionを実行
↓
dispatchでreducerに渡る
↓
reducerで加算が行われてStoreを書き換え（countValueを加算）
↓
Storeが書き換えられるのでコンポーネント内のthis.props.countValueが変化
↓
コンポーネントが書き換えられてカウント：の表示値が増える

という流れになります。これですべてのソースが完成となります！お疲れさまでした。

---
もっとも基本的なReduxの作成は以上になります。理解いただけましたでしょうか。
dispatchとcreateStore辺りの概念が難しくてなかなか理解が難しいですが、とりあえずは「Reduxお決まりの書き方」くらいの認識でとりあえずまるっと覚えてしまって問題ないと思います。

今回のサンプルは冒頭にも書いた通りReact単体でも出来ることで、結果ただソースが増えて複雑になっただけのデメリットしかありません。。
とはいえ、Reduxの導入により拡張性が大幅に上がっていますので、次回はこれを拡張してReduxならではのメリットを組み込みたいと思います。