-
Notifications
You must be signed in to change notification settings - Fork 18
Step2:reduxのstoreから値を受け取り表示する
reactではコンポーネントが様々な状態(state)を持つことによって動的にページが変化します。
redux-pluto ではその state を Redux によって管理しています。
今回はReduxによる state 管理によって動的なページを作成してみましょう。
state(状態)を管理をするためのフレームワークで主に以下の要素を持つ。
Action: 「何をする」という情報を持ったオブジェクト、type プロパティを必ず持つ
ActionCreator: Actionを作成するメソッド
Store: アプリケーションの状態(state)を保持している場所
State: アプリケーションの状態を表す
Reducer: actionとstateから、新しいstateを作成して返すメソッド
stete 管理の大雑把な概要は以下の通り。state は Storeに保持されている。
state を更新したい場合は ActionCreator で Action を発行し、それを Store に dispatch することで reducer を走らせる。Redux DevTools を Chrome に追加しておくと、Store の状態をリアルタイムに確認することができます。
STEP1で作成したコンポーネントで使うための state を管理する module を作成します。
ここではコンポーネント内の文言の表示・非表示の状態を管理する state と、それを切り替える reducer を作成していきます。
/shared/redux/modules 配下に以下の hello.js を追加しましょう。
shared/redux/modules/hello.js
+/* @flow */
+import { createAction, handleActions } from "redux-actions";
+
+ /**
+ * Action types
+ */
+const HELLO = "redux-pluto/hello";
+const HELLO_CHANGE_VISIBILITY = `${HELLO}/visibility/change`; // 表示・非表示を切り替える Action の type
+
+ /**
+ * Action creators
+ */
+export const changeVisibility = createAction(HELLO_CHANGE_VISIBILITY); // 表示・非表示を切り替える Action の ActionCreator
+
+ /**
+ * Initial state
+ */
+ // module 内で管理する state の型
+ export type State = {
+ isVisible: boolean,
+};
+
+ // store に展開される初期値
+ const INITIAL_STATE = {
+ isVisible: true,
+};
+
+ /**
+ * Reducer
+ */
+export default handleActions(
+ {
+ [HELLO_CHANGE_VISIBILITY]: state => ({ // HELLO_CHANGE_VISIBILITY が dispatch された時に走る reducer
+ ...state,
+ isVisible: !state.isVisible,
+ }),
+ },
+ INITIAL_STATE,
+);
利用module
redux-actions
createActions: 引数に action type をとり ActionCreators を返す
handleActions: 第一引数に reducerMap 第二引数に defaultState をとり複数の reducer を作成、それらを複数の action を処理する単一の reducer にまとめてくれる
/shared/redux/modules/reducer.js に 先ほど作成した hello.js をインポートして、 ページスコープのReducer(page配下)に追加します。
reducer は app と page にわかれており、それぞれアプリケーション全体で使われるものか、該当ページにのみ利用されるかのスコープによって使い分けます。
shared/redux/modules/reducer.js
~~~
+ import hello, { type State as Hello } from "./hello";
~~~
export type State = {
~~~
page: {
+ hello: Hello,
~~~
export default combineReducers({
~~~
page: pageScopeReducer(
combineReducers({
+ hello,
~~~
利用module
redux-page-scope
historyEvent に応じた react-router の Action を見て state を初期化したり cache したりする 特定の page に閉じている state に利用する
ここまでできたらRedux DevTools を確認してみましょう。
先ほど作成した module の state が反映されていることがわかります。
Store の内容を画面に表示するには、React コンポーネントの中で Store の持つ state をコンポーネントの props として反映させる必要があります。
これには react-redux の connect を利用します。
react-redux connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
文字通り react componentとreduxのstoreをつなぐために利用される。mapStateToProps(state, ownProps) store.getState()の結果を第一引数,Container componentへ渡されたpropsを第二引数にして呼び出される関数 これらのstateとpropsを使ってPresentational componentにpropsとして渡す値を返す
mapDispatchToProps(dispatch) store.dispatchを第一引数にして呼び出される関数 Presentational componentにpropsとして渡す store に dispatch して state を更新するための関数を返す
src/shared/components/organisms/Hello/index.js
+/* @flow */
import React from "react";
+import { compose } from "recompose";
+import { connect } from "react-redux";
+import { changeVisibility } from "../../../redux/modules/hello";
-export default function Hello(props) {
- return <div>Hello!</div>;
-}
+type Props = { // props の型定義
+ isVisible: boolean,
+ onChangeVisibility: Function,
+};
+
+export default compose(
+ connect(
+ state => ({
+ isVisible: state.page.hello.isVisible, // store の state の中から、指定した isVisible を props として渡す
+ }),
+ dispatch => ({
+ onChangeVisibility: () => dispatch(changeVisibility()), // changeVisibilityを store に dispatchする関数を返す
+ }),
+ ),
+)(function Hello(props: Props) {
+ const { isVisible, onChangeVisibility } = props;
+ return (
+ <div>
+ {isVisible && <div>Hello!</div>}
+ <button type="button" onClick={() => onChangeVisibility()}>
+ {isVisible ? "hide" : "show"}
+ </button>
+ </div>
+ );
+});
Higher-order Componentsを作成・提供するための便利関数ライブラリ compose(...Higher-order components)(EnhancedComponent)を用いることで EnhancedComponentが...Higher-order Componentsで拡張されたComponentになる ※Higher Order Component(HOC)とは、単に他のコンポーネントをラップするReactコンポーネントのことで、 HOCにより以下のことが可能になる
- コードの再利用、ロジックの抽象化
- Stateの抽象化と操作
- Propsの操作
http://localhost:3000/helloを開くと、
Hello!の下に「hide」ボタンが追加されているのが確認できます。
「hide」ボタンを押して動作を確認してみましょう。
Hello! の表示が消え、ボタンのラベルが「show」に変化すれば成功です!
このままでも問題なく動きますが、
画面表示のロジックと、データ受け渡し用のロジックが1つのファイルに混在している状態は好ましくないため、それぞれを別のコンポーネントとして分けて作成します。
まずは 先ほどの index.js から画面表示部分だけ切り出した component Hello.js を作成しましょう src/shared/components/organisms/Hello/Hello.js
+/* @flow */
+import React from "react";
+
+type Props = {
+ isVisible: boolean,
+ onChangeVisibility: Function,
+};
+
+export default function Hello(props: Props) {
+ const { isVisible, onChangeVisibility } = props;
+ return (
+ <div>
+ {isVisible && <div>Hello!</div>}
+ <button type="button" onClick={() => onChangeVisibility()}>
+ {isVisible ? "hide" : "show"}
+ </button>
+ </div>
+ );
+}
次に index.js をデータの受け渡しやロジックに専念する Container component にしましょう。 src/shared/components/organisms/Hello/index.js
-/* @flow */
-import React from "react";
import { compose } from "recompose";
import { connect } from "react-redux";
import { changeVisibility } from "../../../redux/modules/hello";
-
-type Props = {
- isVisible: boolean,
- onChangeVisibility: Function,
-};
+import Hello from "./Hello";
~~~
-)(function Hello(props: Props) {
- const { isVisible, onChangeVisibility } = props;
- return (
- <div>
- {isVisible && <div>Hello!</div>}
- <button type="button" onClick={() => onChangeVisibility()}>
- {isVisible ? "hide" : "show"}
- </button>
- </div>
- );
-});
+)(Hello);
問題なく画面が動いているようであれば次のSTEPへ進みましょう!