『Redux』を用いて状態管理をしてみよう！

#概要
前回の記事ではReactのみを用いて状態遷移を行いましたが、今回はReactJSが扱うUIのstate(状態)を管理をするためのフレームワークである__『Redux』__を用いて、前回と同様の機能を実装したいと思います。

前回の記事
[『state』を用いて、入力された文字数をカウントしてみよう！](https://qiita.com/micropig3402/items/a1f233963d012982d2f9)

#パッケージのインストール
まずは、必要なパッケージのインストールを行いましょう！下記コマンドで`redux`と`react-redux`をインストールできます。

`$ yarn add redux react-redux`

#なぜReduxが必要なのか
早速コードを書いていきたいのですが、まずはなぜ__『Redux』__というものが開発されたのかを調べたいと思います。公式のページをGoogle翻訳で訳して簡単にまとめたものを以下に載せます。

> JavaScriptで開発されるシングルページアプリケーションが複雑になるにつれて、管理しなければいけない状態(state)の数が増大していき、新機能開発やバグ修正などがとても大変になります。その複雑さは『変化(mutation)』と『非同期性(asynchronicity)』という２つの概念を混在させていることにより起こるものです。Reactのようなライブラリは、非同期と直接DOM操作の両方を削除することによって、ビューレイヤでこの問題を解決しようとします。しかし、状態(state)の管理はユーザーに任せられたままでした。__『Redux』__はその状態（state）管理の複雑さを解消するために開発されました。

[公式ページより](https://redux.js.org/introduction/motivation)

#Action
では実際にコードを見ながら理解を進めていきましょう！
まずは__『Action』__からです！

__『Action』__：今から『stateをこのように変化させます』といった情報を持っているオブジェクトのことです。

そのオブジェクトの中で、『type』というKeyとそのKeyに対応する値を持つのが『Action』の特徴です。その『type』の値はユニークなものでないといけません。『type』以外は自由に定義して大丈夫です。

```react:例
{
    type: TEXTCHANGE,
    textValue: textValue,
    textLength: textLength,
}
```

では続いて、作成したActionを返す関数__『Action Creator』__を書いていきましょう！

```react:src/actions/index.js
export const TEXTCHANGE = 'TEXTCHANGE';

export const textChange = (textValue, textLength) => {
  return {
    type: TEXTCHANGE,
    textValue: textValue,
    textLength: textLength,
  };
};
```

`TEXTCHANGE`はこの後のReducerでも使うので、複数箇所で活用されるデータについては１箇所で定義してそれを再利用しましょう！


#Reducer
__『Reducer』__：Actionが発生した時に、そのActionに組み込まれているtypeに応じて、状態をどう変化させるのかというのを定義したものです。

実際にコードを見てみましょう！

```react:src/reducers/index.js
import {combineReducers} from 'redux';
import text from './text';

export default combineReducers({text});

//複数のreducerを持った場合
//combineReducers({text, foo, baz});
```
`src/reducers/index.js`ではアプリケーション内で実装される__『Reducer』__を1つにまとめる役割があります。今回は実装する__『Reducer』__が1つしかないので`combineReducers({text})`となっておりますが、まとめる__『Reducer』__が複数個ある場合は`combineReducers({text, foo, baz});`のように`combineReducers`にまとめれば大丈夫です！

```react:src/reducers/text.js
import {TEXTCHANGE} from '../actions';

const initialState = {textLength: 0, textValue: 'initial value'};

export default (state = initialState, action) => {
  switch (action.type) {
    case TEXTCHANGE:
      return {textValue: action.textValue, textLength: action.textLength};
    default:
      return state;
  }
};
```
`src/reducers/text.js`ではstateとactionから新しいstateを作成してreturnで返します。ここではstateを更新しているのではなく、新しいstateを作成しているというところがポイントです！


#Store
__『Store』__：アプリケーション内のstate(状態)を保存しているところです。

```react:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore} from 'redux';
import {Provider} from 'react-redux';

import './index.css';
import reducer from './reducers';
import App from './components/App';
import * as serviceWorker from './serviceWorker';

const store = createStore(reducer);

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

// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
```

ここでは、Reducerをもとに『store』を作成します。まずは『store』を作成するための関数である`createStore`をreduxパッケージからimportしましょう。そしてimportしたreducerを`createStore`の引数に使うことで、『store』を定義します。

次に、作成した『store』を、全Componentに渡すための機能を持つ`Provider`という特殊なComponentをreact-reduxからimportしましょう！そして各Componentを`<Provide store={store}></Provider>`でラップすることで『store』のデータを渡すことができるようになります。

#connect
connect関数を用いて、今まで作成してきたstateやActionとComponentとの関連付けを行なって、viewのイベントで状態を遷移させて遷移後の状態を画面に再描画しましょう。

```react:src/components/App.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {textChange} from '../actions';

class App extends Component {
  render() {
    const props = this.props;
    console.log(props);

    return (
      <React.Fragment>
        <div>文字数： {props.textLength}</div>
        <textarea
          type="text"
          onChange={e => props.textChange(e.target.value, e.target.value.length)}
        />
      </React.Fragment>
    );
  }
}

const mapStateToProps = state => {
  return {
    textValue: state.text.textValue,
    textLength: state.text.textLength,
  };
};

const mapDispatchToProps = dispatch => {
  return {
    textChange: (textValue, textLength) =>
      dispatch(textChange(textValue, textLength)),
  };
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

```

stateとactionをComponentに結びつけるために`connect関数`をimportし、Componentの名前である`App`を引数にとり、`mapStateToProps`と`mapDispatchToProps`に結びつけます。

`mapStateToProps`はstateからComponentに必要な情報を抜き出して、propsとしてマッピングする機能を持つ関数です。引数にはstateを取って、どういうオブジェクトをpropsとして対応させるのかといったことを返します。

あるactionが発生した時にreducerにtypeに応じた状態遷移を実行させるための関数が`dispatch`になります。`mapDispatchToProps`では、この`dispatch`関数を引数においてこのComponentにおいて必要な`dispatch`関数を宣言します。今回はtextが入力された際に、入力された文字と文字数を表示したいので、`textChange`をkeyに`textChange関数`を引数に持つ`dispatch関数`を値に定義した、`mapDispatchToProps`を作成します。

#最後に
以上で実装は終わりです。前回の記事と同様のものがブラウザにレンダリングされていると思います。

![ezgif.com-optimize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343290/64bb4986-8bfc-7e3f-c151-113638b9a908.gif)

次回は今まで学習してきた、ReactとReduxを使ってTodoアプリケーションを作成する方法をまとめたいと思います！

#リファレンス

- [公式ページ](https://redux.js.org/)
- [Redux入門【ダイジェスト版】10分で理解するReduxの基礎](https://qiita.com/kiita312/items/49a1f03445b19cf407b7)
- [React+Redux入門](https://qiita.com/erukiti/items/e16aa13ad81d5938374e)
- [redux-actionsについて学ぼう！〜iOSエンジニアが苦しんだReduxの基礎(4)〜](http://grandbig.github.io/blog/2017/01/02/redux-base-4/)
- [たぶんこれが一番分かりやすいと思います React + Redux のフロー図解](https://qiita.com/mpyw/items/a816c6380219b1d5a3bf)
- [【React】redux-actionsでreducerとactionsをすっきりさせよう](https://qiita.com/kousaku-maron/items/3d730a5d86b2e686aea4)
- [Redux 基礎：Action 編](https://qiita.com/yasuhiro-okada-aktsk/items/09ab40af831c6d73491b)