React は Flux のフレームワークで利用すると効果的です。
Redux は Flux を参考に 3つの原則 を強調したフレームワークです。
前回 のアプリケーションに Redux を追加してみましょう。
Learning
- Redux の Action, Reducer, Store の Data Flow を利用する。
- React と Redux で連携 connect を利用する。
Environment
- node: v4.4.5
- npm: v3.9.6
Comment Box Form
- 完成される Source Code のファイルリストです。
$ tree -a -I node_modules
.
├── .babelrc
├── app.js
├── index.html
├── index.js
├── package.json
├── style.css
└── webpack.config.js
Let's hands-on
Setup application
-
git clone
コマンドでアプリケーションをダウンロードします。 -
npm install
コマンドで依存するモジュールをインストールします。
$ git clone https://github.com/ogom/react-comment-box-example.git
$ cd react-comment-box-example
$ git checkout react
$ npm install
Start HTTP Server
-
npm start
コマンドで Webアプリケーション を実行します。 - ブラウザで http://localhost:4000 をロードすると Comment Box Example が表示されます。
$ npm start
$ open http://localhost:4000
(API Server は React Tutorial Example (Express) をご利用ください。)
Add Redux package
- Redux のモジュールをインストールします。
$ npm install redux --save-dev
Add Redux
-
createStore()
で Reducer を設定して Store を作成します。-
store.dispatch()
で Acstion を実行すると State が変更されます。
-
+var Redux = require('redux');
+var createStore = Redux.createStore;
+var store = createStore(function(state, action) {
+ if (state === undefined) {
+ return [];
+ }
+
+ switch (action.type) {
+ case 'show_comments':
+ return action.comments;
+ case 'add_comment':
+ return state.concat([action.comment]);
+ default:
+ return state;
+ }
+});
+store.subscribe(function () {
+ return console.log(store.getState());
+});
+store.dispatch({type: 'show_comments', comments: data});
+store.dispatch({type: 'add_comment', comment: {author: "foo", text: "*bar*"}});
-
store.getState()
をconsole.log
で出力すると State を確認することができます。
Add React Redux package
- ReactRedux のモジュールをインストールします。
$ npm install react-redux --save-dev
Add Provider
-
Provider
にstore
を設定して React と Redux を連携します。
-var data = [
- {id: 1, author: "Pete Hunt", text: "This is one comment"},
- {id: 2, author: "Jordan Walke", text: "This is *another* comment"}
-];
-
-store.subscribe(function () {
- return console.log(store.getState());
-});
-store.dispatch({type: 'show_comments', comments: data});
-store.dispatch({type: 'add_comment', comment: {author: "foo", text: "*bar*"}});
+
+var ReactRedux = require('react-redux');
+var Provider = ReactRedux.Provider;
ReactDOM.render(
- <CommentBox url="http://localhost:3000/api/comments" pollInterval={2000} />,
+ <Provider store={store}>
+ <CommentBox url="http://localhost:3000/api/comments" pollInterval={2000} />
+ </Provider>,
document.getElementById('content')
);
Add Connect
-
mapStateToProps
でstate
をマッピングします。 -
mapDispatchToProps
でdispatch
をマッピングします。 -
connect()
でマッピングとコンポーネントを接続します。
-module.exports = CommentBox;
+var ReactRedux = require('react-redux');
+var connect = ReactRedux.connect;
+
+var mapStateToProps = function(state) {
+ return {data: state};
+};
+
+var mapDispatchToProps = function(dispatch) {
+ return {
+ showComments: function(comments) {
+ dispatch({type: 'show_comments', comments: comments});
+ },
+ addComment: function(comment) {
+ dispatch({type: 'add_comment', comment: comment});
+ }
+ };
+};
+
+module.exports = connect(mapStateToProps, mapDispatchToProps)(CommentBox);
Change Show Comments
-
success
のthis.setState
をthis.props.showComments
に置き換えます。 -
render
のthis.state.data
をthis.props.data
に置き換えます。
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
- this.setState({data: data});
+ this.props.showComments(data);
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
- <CommentList data={this.state.data} />
+ <CommentList data={this.props.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});
Change Add Comment
-
newComments
のthis.setState
をthis.props.addComment
に置き換えます。 -
success
のthis.setState
をthis.props.showComments
に置き換えます。 -
error
のthis.setState
もthis.props.showComments
に置き換えます。
var CommentBox = React.createClass({
// id generated by the server. In a production application you would likely
// not use Date.now() for this and would have a more robust system in place.
comment.id = Date.now();
- var newComments = comments.concat([comment]);
- this.setState({data: newComments});
+ this.props.addComment(comment);
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
- this.setState({data: data});
+ this.props.showComments(data);
}.bind(this),
error: function(xhr, status, err) {
- this.setState({data: comments});
+ this.props.showComments(comments);
console.error(this.props.url, status, err.toString());
}.bind(this)
});
Congrats!
Redux Source
Redux は Data Flow を Action, Reducer, Store に責務を分離するパラダイムです。
createStore
の Source code を読むと、だいたいの流れがイメージできます。
Example
Redux は React がなくても単独で実行することができます。
JS Bin で動作するサンプルを作成しました。
HTML
-
cdnjs.cloudflare.com
のredux
を参照します。
<!DOCTYPE html>
<html>
<head>
<title>JS Bin</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.5.2/redux.min.js"></script>
</head>
<body>
</body>
</html>
JavaScrit
-
var store = createStore()
の 引数がreducer
です。 -
store.dispatch()
の 引数がaction
です。
var createStore = Redux.createStore;
var store = createStore(function(state, action) {
if (state === undefined) {
return 0;
}
switch (action.type) {
case 'PLUS':
return state + action.number;
case 'MINUS':
return state - action.number;
default:
return state;
}
});
store.subscribe(function () {
return console.log(store.getState());
});
store.dispatch({type: 'PLUS', number: 2});
store.dispatch({type: 'PLUS', number: 3});
store.dispatch({type: 'MINUS', number: 4});
store.dispatch({type: 'DEFAULT'});
Console
- 数値が増加するアクションや減少するアクションで State が更新されています。
ECMAScript 2015
ECMAScript 2015 ならもっと簡潔に記述することができます。
import {createStore} from 'redux'
const store = createStore((state = 1, action) => action.type === 'PLUS' ? state + 1 : state - 1)
store.subscribe(() => console.log(store.getState()))
store.dispatch({type: 'PLUS'})
Data Flow を Action, Reducer, Store に分離できました。
次は React tutorial で ECMAScript 2015 を使用しましょう!