reduxでundo/redoをする

# undo/redoとは??

もしかしたら`undo/redo`という言葉に馴染みがない人もいるかもなので・・・

- `undo` = 実行した処理を取り消す（元に戻す）
- `redo` = `undo`によって取り消した処理を再度実行する

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

https://github.com/omnidan/redux-undo

## バージョン

- redux-undo: `0.5.0`

## インストール

```
npm install --save redux-undo
```

## 使い方

### reducerまわり

アプリケーション全体の`reducer`をひとまとめにした`Reducer`というものがあるとき、以下のようにその`Reducer`を`undoalbe`で囲ってあげます

```js:reducers.js
import { combineReducers } from 'redux';

import some from './some';
import hoge from './hoge';
import fuga from './fuga';

const Reducer = combineReducers({
  some,
  hoge,
  fuga
});

export default Reducer;
```

```js:index.js
import undoable from 'redux-undo';

const rootReducer = combineReducers({
  router : routerStateReducer,
  appData : undoable(Reducer) // undoalbeで囲う
});
```

この時点で一旦実行してみると、、、`state.appData`の中身が

```js
{
  future: Array[0],
  history: Object,
  past: Array[2],
  present: Object,
}
```

こんな感じになります。なるほど!!

`redux`の大きな特徴の１つとして、`Single State Tree`というものがあります。つまりアプリケーションの`state`を１つのオブジェクトのような形でもつ感じになります。
その`state`を、上の`future`だとか`past`の配列に突っ込んでは出し入れすることで、`undo/redo`を実現している感じですね。
`Single State Tree`だからこそ成せる技、ということだと思います（`action`を`dispatch`することでのみ`state`を変更することができる、などのポイントも含めてだとは思いますが）。

さて、アプリケーションの現時点の`state`は`present`の中におさめられているので、一部コードに修正が必要になります。以下は`state`を`props`に紐づける処理の中を修正しています

```js:App.js
function mapStateToProps(state) {

  const { some, hoge, fuga } = state.appData.present; // presentにする

  return {
      some,
      hoge,
      fuga
  };
}
```

### actionまわり

次に`undo/redo`のトリガーとなる部分の実装です。`redux-undo`は`ActionCreators`という`action`を提供しています（名前がわかりづらい汗）。
以下のように`dispatch`するだけでOKです

```js:Some.js
import React, { Component } from 'react';
import { ActionCreators }   from 'redux-undo';

class Some extends Component {

  onUndo() {
    store.dispatch(ActionCreators.undo()); // undoする
  }

  onRedo() {
    store.dispatch(ActionCreators.redo()); // redoする
  }

  render() {
    return (
      <button onClick={this.onUndo.bind(this)}>undo</button>
      <button onClick={this.onRedo.bind(this)}>redo</button>
    )
  }
};

export default Some;
```

これだけで`undo/redo`が実現できます

## もう少しゴニョゴニョしてみる

必要ないかもしれないけどもう少しゴニョゴニョしてみる

### 子の`reducer`に`undoalbe`を適用してみる

さきほどはアプリケーション全体の`Reducer`に`undoalbe`を適用したが、以下のように子（？）のreducerの１つに`undoalbe`を適用してみる

```js:reducers.js
import { combineReducers } from 'redux';

import some from './some';
import hoge from './hoge';
import fuga from './fuga';

const Reducer = combineReducers({
  undoable(some),
  hoge,
  fuga
});

export default Reducer;
```

```js:index.js
import undoable from 'redux-undo';

const rootReducer = combineReducers({
  router : routerStateReducer,
  appData : Reducer
});
```

```js:App.js
function mapStateToProps(state) {

  const { some, hoge, fuga } = state.data;

  return {
      some : some.present,
      hoge,
      fuga
  };
}
```

この場合は、`some`の中に`future`,`past`,`present`などが生成される形になり、正しく`undo/redo`もできるが、状態の履歴は`some`以外の`state`変更時のものも含まれる形になります


### `undoalbe`を複数の`reducer`に適用してみる

```js:reducers.js
import { combineReducers } from 'redux';

import some from './some';
import hoge from './hoge';
import fuga from './fuga';

const Reducer = combineReducers({
  undoable(some),
  undoable(hoge),
  fuga
});

export default Reducer;
```

これも同じで、`some`,`hoge`の中に`future`,`past`,`present`などが生成される形になり、正しく`undo/redo`もできるが、状態の履歴は`some`,`hoge`以外の`state`変更時のものも含まれる形になります

## 参考

http://rackt.org/redux/docs/recipes/ImplementingUndoHistory.html