本章では(おそらくここまでで最も難しい章なのですが)、アプリケーションに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)