ReactJS + Redux + redux-thunk で非同期のカウンターのサンプル


## 目的

redux-thunk でどのように非同期処理をするのか試してみる。
カウンターの処理は以前書いたものを流用（[ReactJS + Redux + ES6 のカウンターのサンプル](http://qiita.com/search?q=redux+user%3Acortyuming)）



## ファイル構成

```
.
├── dest
│   └── bundle.js
├── index.html
├── package.json
└── src
    └── app.jsx
```

## package


```pacage.json
{
  "name": "react-flux-redux-thunk",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "watch": "watchify src/app.jsx -o dest/bundle.js -v --debug",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "babelify": "^7.2.0",
    "browserify": "^13.0.0",
    "watchify": "^3.7.0"
  },
  "dependencies": {
    "react": "^15.0.1",
    "react-dom": "^15.0.1",
    "react-redux": "^4.4.5",
    "redux": "^3.4.0",
    "redux-thunk": "^2.0.1"
  },
  "browserify": {
    "transform": [
      [
        "babelify",
        {
          "presets": [
            "es2015",
            "react"
          ]
        }
      ]
    ]
  }
}
```

## インストールとビルド

```
$ npm install
$ npm run watch
```

## code


```src/app.jsx
import React, { PropTypes } from 'react';
import thunk from 'redux-thunk';
import { render } from 'react-dom';
import { applyMiddleware, createStore } from 'redux';
import { Provider, connect } from 'react-redux';

// Action
function actionIncrement() {
  return {
    type: 'INCREMENT',
    count: 1
  };
}
function actionIncrementAsync() {
  return dispatch => {
    setTimeout(() => {
      dispatch(actionIncrement());
    }, 1000);
  };
}

function actionDecrement() {
  return {
    type: 'DECREMENT',
    count: -1
  };
}
function actionDecrementAsync() {
  return dispatch => {
    setTimeout(() => {
      dispatch(actionDecrement());
    }, 1000);
  };
}

// Reducer
function counterReducer (state = {count: 0}, action) {
  let count = state.count;
  switch (action.type) {
    case 'INCREMENT':
      return {count: count + action.count};
    case 'DECREMENT':
      return {count: count + action.count};
    default:
      return state;
  }
}


// Component
class CounterComponent extends React.Component {
  render () {
    const { count, onClickPlus, onClickMinus } = this.props;
    return (
      <div>
        <p><span>Count: {count}</span></p>
        <div>
          <button onClick={onClickPlus}>+1</button>
          <button onClick={onClickMinus}>-1</button>
        </div>
      </div>
    );
  }
}

CounterComponent.propTypes = {
  count: PropTypes.number.isRequired,
  onClickPlus: PropTypes.func.isRequired,
  onClickMinus: PropTypes.func.isRequired,
};


// Containers
function mapStateToPropsContainer(state) {
  return {
    count: state.count
  };
}

function mapDispatchToPropsContainer(dispatch) {
  return {
    onClickPlus: () => dispatch(actionIncrementAsync()),
    onClickMinus: () => dispatch(actionDecrementAsync()),
  };
}

let App = connect(
  mapStateToPropsContainer,
  mapDispatchToPropsContainer
)(CounterComponent);


// main
let store = createStore(
  counterReducer,
  applyMiddleware(thunk)
);

render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('app-container')
);
```

## 表示

```
$ python -m SimpleHTTPServer 3000
```

- http://localhost:3000

![スクリーンショット 2015-11-07 14.48.21.png](https://qiita-image-store.s3.amazonaws.com/0/41656/b7055cbf-05b3-2817-c15f-20cbbea0c1e9.png "スクリーンショット 2015-11-07 14.48.21.png")

ボタンを押すと1秒遅れて表示にカウント数が変わる



## 参考

- https://github.com/rackt/redux
- https://github.com/gaearon/redux-thunk