本章では(おそらくここまでで最も難しい章なのですが)、アプリケーションにReduxを追加し、Reactと連携させます。Reduxはアプリケーションの状態を管理するもので、アプリの状態を表現する素のJavaScriptオブジェクトであるstore、一般的にユーザが起点となるaction、アクションハンドラにも見えるreducerが合わさったものです。reducerはアプリケーションの状態(store)に影響を及ぼし、アプリケーションの状態が変更がされると、アプリケーションに何かが起こります。Reduxの分かりやすい動くデモがここにあります。
Reduxを可能な限りシンプルに扱うにはどうすればよいか説明するため、アプリはメッセージとボタンで構成されるようにします。メッセージは犬が鳴いているかどうか(最初は鳴いていません)を伝え、ボタンを押すと犬が鳴き、メッセージが更新されるようにします。
ここでは、redux
とreact-redux
の2つのパッケージが必要になります。
yarn add redux react-redux
を実行します。
はじめに、2つのフォルダ、src/client/actions
とsrc/client/reducers
を作ります。
actions
内に、以下のdog-actions.js
ファイルを作ります:
export const MAKE_BARK = 'MAKE_BARK';
export const makeBark = () => ({
type: MAKE_BARK,
payload: true,
});
ここではアクションタイプMAKE_BARK
を定義しています。また、MAKE_BARK
アクションのトリガーとなる関数(これはaction creatorとも言います)makeBark
も定義します。どちらも他のファイルから利用するため、exportされています。このアクションはFlux Standard Actionモデルで実装されています。type
属性とpayload
属性を持っているのはそのためです。
reducers
フォルダに以下のようなdog-reducer.js
ファイルを作ります:
import { MAKE_BARK } from '../actions/dog-actions';
const initialState = {
hasBarked: false,
};
const dogReducer = (state = initialState, action) => {
switch (action.type) {
case MAKE_BARK:
return { hasBarked: action.payload };
default:
return state;
}
};
export default dogReducer;
ここではアプリの初期状態として、hasBarked
プロパティがfalse
になっている状態を定義しています。また、どのアクションが実行されたかによって状態を作る関数dogReducer
も定義しています。この関数は状態を変更することがありません。代わりに新しい状態オブジェクトを返します。
- storeを作るため
app.jsx
を修正します。ファイルの中身全体を以下の内容に置き換えます:
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import dogReducer from './reducers/dog-reducer';
import BarkMessage from './containers/bark-message';
import BarkButton from './containers/bark-button';
const store = createStore(combineReducers({
dog: dogReducer,
}));
ReactDOM.render(
<Provider store={store}>
<div>
<BarkMessage />
<BarkButton />
</div>
</Provider>
, document.querySelector('.app')
);
見ると明らかなように、Reduxの関数createStore
がstoreを作ります。storeオブジェクトはReduxのcombineReducers
関数を使って、reducerを全て(この例では1つだけですが)集めたものです。各reducerはここで名前がつけられます。ここではdog
という名前をつけています。
ここは純粋なReduxの部分です。
それではreact-redux
を使ってReduxとReactを結びつけましょう。react-redux
がReactアプリにstoreを渡せるようにするため、アプリ全体を<Provider>
コンポーネントで包みます。このコンポーネントは1つだけしか子要素を持てません。そのためここでは<div>
要素を作り、この<div>
要素にアプリの2つの重要な要素であるBarkMessage
とBarkButton
を持たせるようにします。
import
セクションにある通り、BarkMessage
とBarkButton
はcontainers
フォルダからimportしたものです。ここはComponents と Containersという2つのコンセプトを紹介するよい機会です。
Component は 賢くない(dumb) Reactコンポーネントです。これはReduxの状態について何も知らないという意味です。Containers は *賢い(smart)なコンポーネントです。これは状態を知っており、他の賢くないコンポーネントに接続(connect)*できるためです。
-
src/client/components
とsrc/client/containers
の2つのフォルダを作ります。 -
components
の中に、以下のファイルを作ります:
button.jsx
import React, { PropTypes } from 'react';
const Button = ({ action, actionLabel }) => <button onClick={action}>{actionLabel}</button>;
Button.propTypes = {
action: PropTypes.func.isRequired,
actionLabel: PropTypes.string.isRequired,
};
export default Button;
そして、message.jsx:
import React, { PropTypes } from 'react';
const Message = ({ message }) => <div>{message}</div>;
Message.propTypes = {
message: PropTypes.string.isRequired,
};
export default Message;
これらは賢くないコンポーネントの例です。ロジックがなく、Reactのprops経由で問い合わせが来たものをそのまま返すだけのものです。button.jsx
と message.jsx
の主な違いは、Button
はそのプロパティとしてアクション(action) を持っていることです。アクションはonClick
イベントに結び付けられています。アプリのコンテキストで、Button
ラベルは変化することがありませんが、Message
コンポーネントはアプリの状態を反映し、状態によって変化します。
繰り返しますが、 コンポーネント はReduxの アクション やアプリの状態 について何も知りません。そのため、適切なactions と dataを2つの賢くないコンポーネントに伝えるための賢いコンテナを作ることになるのです。
containers
内に、以下のファイルを作ります:
bark-button.js
import { connect } from 'react-redux';
import Button from '../components/button';
import { makeBark } from '../actions/dog-actions';
const mapDispatchToProps = dispatch => ({
action: () => { dispatch(makeBark()); },
actionLabel: 'Bark',
});
export default connect(null, mapDispatchToProps)(Button);
そして bark-message.js:
import { connect } from 'react-redux';
import Message from '../components/message';
const mapStateToProps = state => ({
message: state.dog.hasBarked ? 'The dog barked' : 'The dog did not bark',
});
export default connect(mapStateToProps)(Message);
BarkButton
はButton
とmakeBark
アクション、そしてReduxのdispatch
メソッドを結びつけます。また、BarkMessage
はアプリケーションの状態とMessage
を結びつけます。状態が変更されると、Message
は自動的に適切なmessage
属性を再描画します。これらはreact-redux
のconnect
関数によって動作します。
yarn start
を実行し、index.html
を開きます。そうすると、"The dog did not bark"とボタンが表示されます。ボタンをクリックすると、"The dog barked"というメッセージが表示されるはずです。
(原文: 9 - Redux)