React/Reduxの導入


自分なりのRedux導入法を紹介したいと思います。  
Reduxとは？についてはすでに色々な記事があると思うので、自分で探してください。  
`create-react-app` を使ってカウンターアプリを作って説明していきます。


## Reactアプリ作成

```
$ create-react-app counter-app
```

## パッケージインストール

Reduxアーキテクチャを実現するために必要なパッケージをインストールします。


```
$ yarn add prop-types redux react-redux
```

## Reduxの導入

### 1. Store
状態(state)を保持する場所で、状態とはjsonデータが集まったもののイメージです。

index.jsファイルを編集します。
`rootReducers` はこれから用意しますので、気にしないでください。

```js
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import { createStore } from 'redux';
import rootReducer from './reducers/index';
import { Provider } from 'react-redux';

// Storeの作成
const store = createStore(rootReducer);

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

```

### 2. Actions

よくActionTypesとActionsを分けたりするみたいですが、
個人的にはActionsは状態ごとに分け、一緒に定義しています。

[GitHub - redux-utilities/flux-standard-action: A human-friendly standard for Flux action objects.](https://github.com/redux-utilities/flux-standard-action)

によると、

```js
{
  type: 'ADD_TODO',
  payload: new Error(),
  error: true,
  meta: 'optional values'
}
```

と4つのpropertyがあって

- type: (必須) Actionのタイプ
- payload: Actionで渡ってきた引数など。errorがtrueの場合はエラーオブジェクトにすべき。
- error: エラーかどうか
- meta: なんでも良い。追加情報など。

だそうです。  
今回はとくにActionから渡したい値もないので、typeだけ定義します。



```js
// src/actions/counter.js
// Action Types
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';

// Actions
export function increment() {
  return { type: INCREMENT }
}

export function decrement() {
  return { type: DECREMENT }
}
```


### 3. Reducers

Reducerは定義したActionを元に状態の変更を行います。
Reducer(還元剤)なので、変更後の状態は副作用のないものでなければなりません。

reducersディレクトリを作成、counter.jsとindex.jsファイルを作成します。

CounterのReducerを用意。

```js
// src/reducers/counter.js
import { INCREMENT, DECREMENT } from '../actions/counter';

const initialState = {
  count: 0
};

export function counterReducer(state = initialState, action){
  switch(action.type) {
    case INCREMENT:
      return {
        ...state,
        count: ++state.count
      };
    case DECREMENT:
      return {
        ...state,
        count: --state.count
      };
    default:
      return state;
  }
}
```

全てのReducerを取りまとめるRootのReducerを用意。

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

const rootReducer = combineReducers({
  // 状態ごとのReducer
  counterReducer,
})

export default rootReducer;
```

### 5. Container Components
Reduxを取り入れたReact Componentには2種類に分けられ、
Presentational ComponentsとContainer Componentsがあります。
簡単にいうと

| |Presentational Components| Container Components|
|---|---|---|
|目的|どのように見えるか|どのような動きになるか|
|Reduxを意識した作り|NO|YES|
|データの読み込み|propsから|Redux stateから|
|データの変更|propsからcllbacksを呼ぶ|Redux actionsをdispatchする|
|書かれ方|手動で|React Reduxから生成される|

という風に区別でき、
Presentational ComponentsはReduxを意識せず、見た目だけにフォーカスしたコンポーネントで、
Container ComponentsはそのPresentational Componentsとこれまで用意したReduxアーキテクチャ(Reducers, Actions, Store)をつなぐためのコンポーネントなイメージです。

`Counter` というコンポーネントが出てきていますが、
これはPresentational Componentsで次に説明しますので、一旦無視してください。

  

App.jsを編集します。

```jsx
// src/App.js
import React, { Component } from 'react';
import './App.css';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { increment, decrement } from './actions/counter';
import Counter from './components/counter';

class App extends Component {
  render() {
    return (
      <div className="App">
        <Counter {...this.props} />
      </div>
    );
  }
}

// 型チェック
App.propTypes = {
  counter: PropTypes.object.isRequired,
  dispatch_increment: PropTypes.func.isRequired,
  dispatch_decrement: PropTypes.func.isRequired
}

// state => props
function mapStateToProps(state) {
  return {
    counter: state.counterReducer,
  }
}

// dispatch => props
function mapDispatchToProps(dispatch) {
  return {
    dispatch_increment: () => dispatch(increment()),
    dispatch_decrement: () => dispatch(decrement())
  }
}

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


### 5. Presentational Components

先ほど出てきた `Counter` がカウンターアプリの見た目部分を担当しているコンポーネントです。

componentsディレクトリを作成、その下にcounter.jsファイルを作成します。


```jsx
// src/components/counter.js
import React, { Component } from 'react';

class Counter extends Component {
  render() {
   let { counter, dispatch_increment, dispatch_decrement } = this.props;
   return (
     <div>
       <div>{counter.count}</div>
       <button onClick={dispatch_increment}>+</button>
       <button onClick={dispatch_decrement}>-</button>
     </div>
   );
  }
}

export default Counter;
```

ご覧の通り、Presentational Componentsからは、Actionの切り替えやstateにあるデータの読み込みをpropsから行っており、見た目部分に集中できています。


### 完成

![react_redux.mov.gif](https://qiita-image-store.s3.amazonaws.com/0/52622/9b6324ba-acf9-b1bc-ac21-47d8d9723c71.gif)


## 番外編: Redux Dev Tools

せっかくReduxを導入したので、開発ツールを使いましょう。
これで飛躍的にデバッグがしやすくなります！

[Redux Devtools Extensionを使った時のこの感動を伝えたい - Qiita](https://qiita.com/daiki7nohe/items/fa0f496eebb0980f86da)

## まとめ
以上で自分なりのRedux導入方法を書いてみました。
`redux-actions` 、 `redux-saga` なども使うと良いですが、今回は基本的な部分だけを紹介しました。