Reduxさわってみたのでメモ 【middleware, ログ、非同期処理】

# `redux-logger`を使ってみる

https://github.com/fcomb/redux-logger

## redux-logger

`State`がどう変わったか、及びどの`Action`がdispatchされたことによるか、をログとして表示してくれる`Middleware`。

ソースみてもわかるように、

```js:redux-logger/blob/master/src/index.js
    if (level) {
      console[level](`%c prev state`, `color: #9E9E9E; font-weight: bold`, prevState);
      console[level](`%c action`, `color: #03A9F4; font-weight: bold`, formattedAction);
      console[level](`%c next state`, `color: #4CAF50; font-weight: bold`, nextState);
    } else {
      console.log(`%c prev state`, `color: #9E9E9E; font-weight: bold`, prevState);
      console.log(`%c action`, `color: #03A9F4; font-weight: bold`, formattedAction);
      console.log(`%c next state`, `color: #4CAF50; font-weight: bold`, nextState);
    }
```

以下のようなログを表示してくれる

![687474703a2f2f692e696d6775722e636f6d2f7168637a314f442e706e67.png](https://qiita-image-store.s3.amazonaws.com/0/50827/cb48de53-6962-3a20-3cea-da3a4b52f6e9.png "687474703a2f2f692e696d6775722e636f6d2f7168637a314f442e706e67.png")

## version

- `2.0.2`

## install

```
npm install --save redux-logger
```

## usage

```js:index.js
import createLogger from 'redux-logger';

const logger = createLogger();
const createStoreWithMiddleware = applyMiddleware(logger)(createStore);
const store = createStoreWithMiddleware(reducer);
```

オプションもいろいろあるようです

```js:index.js
const logger = createLogger({
  // 特定の種別のアクション以外のログを表示する
  predicate: (getState, action) => action.type !== AUTH_REMOVE_TOKEN,
  // ログレベルを指定 
  level: 'info',
  // 一連の処理にかかった時間を表示するか
  duration: true 
});
```

## memo

普通に`README.md`みれば問題なく動くはずなのだけど、自分は

https://github.com/rackt/redux/tree/master/examples/async

を参考にしながら実装してしまったのですが、`redux-logger`のバージョンが`0.0.3`とかなり古く、少しハマってしまいました（下のコメント部分）

```js:configureStore.js
import thunkMiddleware from 'redux-thunk';
import loggerMiddleware from 'redux-logger';
import rootReducer from '../reducers';

const createStoreWithMiddleware = applyMiddleware(
  thunkMiddleware,
  loggerMiddleware // loggerMiddleware() のように関数ではない!!!
)(createStore);

export default function configureStore(initialState) {
  const store = createStoreWithMiddleware(rootReducer, initialState);

  // ...
```

# `isomorphic-fetch`を使ってみる

さきほどのサンプルの[Async](https://github.com/rackt/redux/tree/master/examples/async)では非同期処理に`isomorphic-fetch`を使用していたので、それに倣って使ってみた

https://github.com/matthew-andrews/isomorphic-fetch

## version

- isomorphic-fetch => `2.1.1`
- es6-promise => `3.0.2`

※ `es6-promise`は`promise`使うためのポリフィル

## install

```
npm install --save isomorphic-fetch es6-promise
```

## usage

```js:SomeComponent.js
  componentDidMount() {
    fetch('http://localhost:7000/list')
      .then(response => response.json())
      .then(json => store.dispatch(Actions.updateList(json)));

    // ...
```

こんな感じで、非同期処理でデータを引っ張ってきて、そのデータを引数に`Action`をdispatchする、というのは特に迷うことなくできたのですが・・・サンプルでは`Action`の中に非同期処理を入れ込んでいるようです。

ということで、サンプルに倣って新規に追加した`Action`をdispatchするよう修正してみっところ、

```js:Action.js
export function fetchList = () => {
  return dispatch => {
    return fetch('http://localhost:7000/list')
      .then(response => response.json())
      .then(json => dispatch(Actions.updateList(json)));
  };
}
```

以下のエラーがでました

> Uncaught Error: Actions must be plain objects. Use custom middleware for async actions.

内容は御尤ものことで、`Action`はオブジェクトでないとダメというもの

# `redux-thunk`を使ってみる

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



## version

- `1.0.0`

## install

```
npm install --save redux-thunk
```

## usage

`Middleware`に追加する

```js:index.js
import createLogger from 'redux-logger';
import thunk        from 'redux-thunk';

const logger = createLogger();
const createStoreWithMiddleware = applyMiddleware(thunk, logger)(createStore);
const store = createStoreWithMiddleware(reducer);
```


## メモ

`redux-thunk`を`Middleware`として追加することで、さきほどの`Actions must be plain objects`というエラーはでなくなり、非同期処理・データフローも正しい挙動になりました。

また、この`Middleware`ってコードでいうとたったのこれだけなので、アイデアあれば結構簡単に`Middleware`つくれるかなぁと勇気をもらった

```js:redux-thunk/blob/master/src/index.js
export default function thunkMiddleware({ dispatch, getState }) {
  return next => action =>
    typeof action === 'function' ?
      action(dispatch, getState) :
      next(action);
}
```