概要
post後のパラメータidを取得して、redux-sagaを呼び出すとき、(id) => {this.props.history.push(
/items/${id})
としてcallbackをaction.payload
で渡して、実行すればOK
使いどころ
- 新規アイテム作成ダイアログにおいて、アイテム名入力後に自動的にアイテム表示画面に遷移させる
- ユーザー登録画面において、ユーザー作成後に自動的にユーザー情報表示画面に遷移させる
実現したいページ遷移イメージ
(今回のコードに色々くっつければ、こんな感じのページ遷移になるよというイメージ)
環境
- react-router: 4.2.0
- react-router-dom: 4.2.2
- redux-saga: 0.16.0
- react-routerでhisotry.pushができるようになっている。参考
- (サーバー側はrailsのwebpackerつかってます。)
方法
想定する状況は、redux-sagaでpostが既に実装されていて、そこにページ遷移機能を追加するときです。
説明の都合上postの実装も前準備で説明します。
前準備
(本質ではないので読み飛ばしてもOK)
api
postのapiを準備します。サーバー側のコードは省略します。動きだけを説明すると
- あるitemのnameをpostする
- postが成功するとサーバー側では新たなidを持つitemが作成される。
- レスポンスとして、response.data.statusには'ok'、result.idにidがかえってくる
apiのコードは下記の通り。至って普通。
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を作成するためのパラメータですが、それぞれの実装に合わせて適宜変えてください。)
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)
の箇所を追加するだけだとおもいます。
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を呼び出すようにします。
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))
参考
雑談
- redux-saga覚えたおかげでreactが楽しくなった。viewからロジックをsagaにうつすとcomponentがすっきりするから、書いていてつらくない。
- react-routerを使うと、railsのノリでSPAが書けるので設計が楽になった。