概要
React + Redux の Action についてサンプルを使って、Action の作成と動作の確認をします。
準備
redux/examples/real-world
からいろいろそぎ落としたこちらのソースの tag/init
を使って説明します。
※ 実装完了はtag/action
Action とは
Action はストアの state を変更するためのメッセージです。
Action によって state が変わると UI の表示などアプリの変化が起きるので、アプリに何か起こすための出発点となります。
- Action 発行
- Reducer が現在の state と action を元に新しい state を作成。
- state の変更をUIなどに反映
Action は javascipt のオブジェクトでどのような形でも大丈夫ですが、慣習として type
フィールドに文字列で Action のタイプを指定します。 type
フィールド以外は自由に定義してください。
{
type: "SHOW_ERROR",
error: message
}
Action の作成
今回のサンプルには errorMessage
という Reducer があります。
function errorMessage(state = null, action) {
const { type, error } = action;
if (type === ActionTypes.RESET_ERROR_MESSAGE) {
return null;
}
if (error) {
return error;
}
return state;
}
この Reducer は action.type
が RESET_ERROR_MESSAGE
のとき、新しい状態として null を返し、action
に error
があるとき 新しい状態として error
を返します。
アプリはこの状態を UI にエラーメッセージとして表示します。
この Reducer が処理できる Action を発行します。
まず、Action を発行するには ActionCreator を作ります。ActionCreator は Action を返すだけの単純な関数です。
actions/index.js
にすでに resetErrorMessage
という ActionCreator があるので同じように showErrorMessage
を作ります。
export const SHOW_ERROR_MESSAGE = 'SHOW_ERROR_MESSAGE';
export function showErrorMessage(message) {
return {
type: SHOW_ERROR_MESSAGE,
error: message
}
}
Action の発行
Action を発行するには、store.dipatch(action)
に Action を渡しますが、react-redux の connect
を使うと ActionCreator (と同じ形の function) で自動的に発行できます。
containers/App.js
にすでに resetErrorMessage
のコードがあるので、同じように connect
に showErrorMessage
を追加します。
import { resetErrorMessage, showErrorMessage } from '../actions';
export default connect(mapStateToProps, {
resetErrorMessage,
showErrorMessage,
pushState
})(App);
connect
が props に showErrorMessage (と同じ形の function)をマッピングするため、その function を呼んで発行します。
handleShowError(e) {
this.props.showErrorMessage("sample error!!");
e.preventDefault();
}
renderSample() {
return (
<ul>
<li><a href="#" onClick={::this.handleShowError}>show error</a></li>
</ul>
);
}
show error のリンクをクリックするとエラーメッセージが表示されます。 エラー表示後の Dissmiss をクリックするとエラーメッセージを消します。
右のペーンで Action と state の変化が確認できます。
bindActionCreators
Action を追加するたびに connect に追加するのはたいへんなので、bindActionCreators を使って自動的にマッピングします。
connect
を次のように変更します。
import { bindActionCreators } from 'redux';
// import { resetErrorMessage, showErrorMessage } from '../actions'; // 削除
import * as Actions from '../actions';
function mapDispatchToProps(dispatch) {
return {
...bindActionCreators(Actions, dispatch),
pushState
};
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
非同期の Action
1秒後に何かしたいなど、非同期で Action を実行したい場合は、ActionCreator で function (dipatch)
型の function を返します。
export function showErrorMessageDelayed(message, delay = 1000) {
return dispatch => {
setTimeout(() => {
dispatch(showErrorMessage(message));
}, delay);
};
}
さらに store に thunk という middleware を追加します。
import thunk from 'redux-thunk';
const finalCreateStore = compose(
applyMiddleware(thunk),
reduxReactRouter({ routes, createHistory }),
applyMiddleware(createLogger()),
DevTools.instrument()
)(createStore);
Action の発行や Reducer は特別な処理はなく同期型と同じです。
handleShowErrorDelayed(e) {
this.props.showErrorMessageDelayed("delayed sample error!!");
e.preventDefault();
}
最後に
サンプルで遊んでいるときは簡単だと思いましたが、Action がきちんと通るようにするまでが結構たいへんでした。
一度設定してしまえば、あまり変更しない部分なので自分用のテンプレートができると楽になると思います。