カウントアプリを作ってReduxの流れを理解する。

# カウントアプリの概要
<img width="253" alt="スクリーンショット 2019-06-17 12.00.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/252234/ad451462-766c-1cd9-35c6-1b2c1b4237f1.png">
増えたり減ったりします。

##Reactのみでこのアプリを実装。
Reactのみでカウントアプリを作成して、後からReduxを導入します。
*create-react-appでReactアプリを作成してある程で話が進みます*

```
# ディレクトリ構造

react-app/
        ├  node_modules
        ├  public
        ├  src
        │   ├ components
        │   │      ├ App.js
        │   │
        │   ├ index.js
        ├  package.json
        ├  README.md
        ├  yarn.lock
```

###表示部分の実装
```react:src/components/App.js
import React , { Component } from 'react'; // Componentをimportする
class App extends Component{
  render(){
    return(
      <React.Fragment>
        <div>count : 0</div>
        <button>+1</button>
        <button>-1</button>
      </React.Fragment>
    )
  }
}

export default App;
```
class componentを使って実装していくので、importしておく。

App.jsをcomponents配下に移動させたので、src/index.jsも変更する。

```react:src/index.js

import React from 'react';
import ReactDOM from 'react-dom';
import App from './components/App'; //ここを変更

ReactDOM.render(<App />, document.getElementById('root'));
```

これで、動かないが画面への表示される。

次にボタンを押すと、stateが更新され、countの数字が変わるようにしていく。

```react:src/components/App.js

import React , { Component } from 'react';

class App extends Component{

  constructor(props){
    super(props)
    this.state = { count: 0 } //countの初期値を設定
  }

  // プラスボタンが押された時の処理
  handlePlusButton = () => {
    this.setState({ count: this.state.count + 1})
  }

  // マイナスボタンが押された時の処理
  handleMinusButton = () => {
    this.setState({ count: this.state.count - 1})
  }

  render(){
    return(
      <React.Fragment>
        <div>count : { this.state.count }</div>
        <button onClick={this.handlePlusButton}>+1</button>　
        <button onClick={this.handleMinusButton}>-1</button>
      </React.Fragment>
    )
  }
}

export default App;

```
Reactのみのカウントアプリはこれで完成。

##Reduxの導入
Reduxを利用するためにパッケージをinstallする。

```bash
yarn add react react-redux
```

ディレクトリ構造を変更する。

```
react-app/
        ├  node_modules
        ├  public
        ├  src
        │   ├ components
        │   │      ├ App.js
        │   │
        │   ├ actions 
        │   │      ├ index.js 
        │   │
        │   ├ reducers
        │   │      ├ index.js
        │   │      ├ count.js
        │   │
        │   ├ utils
        │   │      ├ index.js
        │   │
        │   ├ index.js
        ├  package.json
        ├  README.md
        ├  yarn.lock
```

###アクション(actions)の作成
アクションには、アプリケーション内部で起こる出来事を記載する。jsのオブジェクトで処理は書かない。
オブジェクト内部には、ユニークな名前を持つ'type'キーと、typeに対応する値を持つ。

```js:src/actions/index.js

export const INCREMENT = 'INCREMENT'  //再利用することが多いので定義。あとでutils/indexに移動します。
export const DECREMENT = 'DECREMENT'  //再利用することが多いので定義。あとでutils/indexに移動します。

//プラスする処理の名前
export const increment = () =>({
    type: INCREMENT
  })

//マイナスする処理の名前
export const decrement = () =>({
    type: DECREMENT
  })
```

ここで、utils/index.jsを作成して、上記のINCREMENTとDECREMENTを定義して、src/actions/index.jsからimportする仕組みにする。

```js:src/utils/index.js

export const INCREMENT = 'INCREMENT'  
export const DECREMENT = 'DECREMENT'  
```

```js:src/actions/index.js

import { INCREMENT, DECREMENT } from './utils' //ここを追加

//プラスする処理の名前
export const increment = () =>({
    type: INCREMENT
  })

//マイナスする処理の名前
export const decrement = () =>({
    type: DECREMENT
  })
```

### reducerの作成
actionが発生した時に、そのactionのtypeに応じて、stateをどう変化させるのか定義する。

```react:src/reducers/index.js

// 全てのreducerをまとめるためのファイル
import { combineReducers } from 'redux' //applicationの全reducerをまとめるために必要
import count from './count'

export default combineReducers({ count })
// export default combineReducers({ count, hoge, huga }) のように書く。これで、count,hoge,hugaが使えるようになる。
```

```react:src/reducers/count.js

import { INCREMENT, DECREMENT } from '../utils'

const initialState = { value: 0 } //状態の初期値を定義。

const count = (state = initialState, action) => {
  switch(action.type){
    case INCREMENT:
      return {value: state.value + 1} //変更したい状態を返す。※ここではまだ状態は変わっていない。
    case DECREMENT:
      return {value: state.value - 1}
    default:
      return state
  }
}

export default count;

```

###Storeの作成
アプリケーションの状態(state)を保持する。

```react:src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore } from 'redux' //storeを作成するために必要
import { Provider } from 'react-redux' //作成したstoreを全componentに渡すために必要

import App from './components/App';
import reducer from './reducers' //作成したreducerをimportしておく

const store = createStore(reducer) //storeの作成

ReactDOM.render(
  <Provider store={store}> 
    <App />
  </Provider>, document.getElementById('root'));
```
これで、redux部分はほとんど完成。

##ReactとReduxのつなぎこみ

```react:src/components/App.js

import React, { Component } from 'react';
import { connect } from 'react-redux'
import { increment, decrement } from '../actions' //actionsのimport


class App extends Component{
  render(){
    const props = this.props //状態やアクションを渡す。mapStateToPropsで定義されている。関数はmapDipatchTopropsで渡されている。
    return(
        <React.Fragment>
        <div>count : { props.value }</div>
        <button onClick={props.increment}>+1</button>
        <button onClick={props.decrement}>-1</button>
        </React.Fragment>

    )
  }
}

// stateの情報からこのcomponentで必要なものを取り出してcomponent内のpropsとしてマッピング機能
// 引数には、状態のトップレベルを示すstateを書いて、どういったオブジェクトをpropsとして対応させるのか、関数の戻り値として定義。
const mapStateToProps = (state) =>{
  return {value: state.count.value}
}

//あるアクションが発生した時に、reducerにタイプに応じた状態遷移を実行させるための関数がdipatche。
//このdispatch関数を引数に置くことで、このcomponentで必要になるdipatche関数を宣言する。
const mapDispatchToProps = (dispatch) =>(
  {
    increment: () => dispatch(increment()),
    decrement: () => dispatch(decrement())
  }
)


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

```
ここでつまづいたのが、
>const props = this.props

これは、mapStateToPropsとmapDispatchToPropsでそれぞれから状態を受け取っていた。
なので、今回だと、valueとincrement関数、decrement関数が渡されている。

##まとめ
ソースコード
https://github.com/satokiyoshino/react-count-app
compileエラーが起きないで、バグが起きるとデバッグがめちゃくちゃ難しい。
Chromeの拡張で、Redux Devtoolがあるので、これを使えば少し助かるかもしれない。
https://github.com/zalmoxisus/redux-devtools-extension