Reactでpost後のレスポンスを使ったページ遷移をredux-sagaとreact-routerで行う方法

# 概要
post後のパラメータidを取得して、redux-sagaを呼び出すとき、`(id) => {this.props.history.push(`/items/${id}`)`としてcallbackを`action.payload`で渡して、実行すればOK

# 使いどころ
* 新規アイテム作成ダイアログにおいて、アイテム名入力後に自動的にアイテム表示画面に遷移させる
* ユーザー登録画面において、ユーザー作成後に自動的にユーザー情報表示画面に遷移させる

# 実現したいページ遷移イメージ
（今回のコードに色々くっつければ、こんな感じのページ遷移になるよというイメージ）
[![https://gyazo.com/186ed16e427f297d3a88ba3b5e7f5892](https://i.gyazo.com/186ed16e427f297d3a88ba3b5e7f5892.gif)](https://gyazo.com/186ed16e427f297d3a88ba3b5e7f5892)

# 環境
* react-router: 4.2.0
* react-router-dom: 4.2.2
* redux-saga: 0.16.0
* react-routerでhisotry.pushができるようになっている。[参考](https://qiita.com/junara/items/a4a98c27dc23fd53ebb9)
* （サーバー側はrailsのwebpackerつかってます。）

# 方法
想定する状況は、redux-sagaでpostが既に実装されていて、そこにページ遷移機能を追加するときです。
説明の都合上postの実装も前準備で説明します。

## 前準備
（本質ではないので読み飛ばしてもOK）
### api
postのapiを準備します。サーバー側のコードは省略します。動きだけを説明すると
1. あるitemのnameをpostする
2. postが成功するとサーバー側では新たなidを持つitemが作成される。
3. レスポンスとして、response.data.statusには'ok'、result.idにidがかえってくる

apiのコードは下記の通り。至って普通。

```app/javascript/modules/Api.js
import axios from 'axios'

const axiosPost = ({url, data}) => {
  return (
    axios.post(url, data)
      .then(
        response => {
          console.log('success');
          return (response);
        }
      )
  )
};

export const createItem = (data) => axiosPost({url: '/api/v1/items', data: data});
```

## 手順
要点は、sagaで実行するためにaction.payloadに関数をわたすこと。このわたす関数をreact-routerの関数にします。そうすれば、sagaのタスク内からreact-routerの関数を実行できます。

### action
渡す関数をcallbackとしてactionを下記のように定義します。（nameは、itemを作成するためのパラメータですが、それぞれの実装に合わせて適宜変えてください。）

```app/javascript/actions/items.js
export const CREATE_ITEM = 'CREATE_ITEM';

export const createItem = (name, callback) => {
  return {
    type: CREATE_ITEM,
    payload: {name: name, callback: callback}
  }
};

```

### saga
sagaのタスク内でaction.payload.callbackを呼び出します。postのレスポンスにidが乗っているのでそれをcallbackの引数にします（それぞれの状況でことなるとはおもいますが）。
既にpost用のコードが書かれていて、idを引数にしたnavigationであれば、`action.payload.callback(id)`の箇所を追加するだけだとおもいます。

```app/javascript/sagas/mySaga.js
import {call, put, takeLatest} from 'redux-saga/effects'
import {createItem} from '../modules/Api'
import {CREATE_ITEM} from '../actions/items'
import 'babel-polyfill' // ジェネレータ関数を使うために必須

export function* createItemFlow(action) {
  if (params.name) {
    const response = yield call(createItem, {name: action.payload.name})
    if (response.data.status === 'ok') {
      const id = response.data.result.id
      action.payload.callback(id) // action.payload.callbackにreact-routerのhistory.pushするcallbackを載せてここで呼び出す
    }
  }
}

function* mySaga() {
  yield takeLatest(CREATE_ITEM, createItemFlow);
}

export default mySaga;
```

### component
最後にcomponentでcallbackにreact-routerの関数をのせてsagaを呼び出すようにします。

```component/ItemCreationDialog.jsx
import React from 'react'
import {connect} from 'react-redux'
import {createItem} from '../actions/items'
import {withRouter} from 'react-router';

class ItemCreationDialog extends React.Component {
// 省略
  handleCreateItem = () => {
	  this.props.createItem(
	    this.state.name, // action.payload.nameに渡される
	    (id) => {this.props.history.push(`/items/${id}`)} // action.payload.callbackに渡される
	  )
  };

  render() {
    return (
    <div>
    	// this.handleCreateItemを呼び出す処理。//
    </div>
    )
  }
}

export default withRouter(connect(state => {return (state)},{createItem})(ItemCreationDialog))
```

# 参考
* https://www.youtube.com/watch?v=EifOGwAW5ZM

# 雑談
* redux-saga覚えたおかげでreactが楽しくなった。viewからロジックをsagaにうつすとcomponentがすっきりするから、書いていてつらくない。
* react-routerを使うと、railsのノリでSPAが書けるので設計が楽になった。