「[[.NET 開発基盤部会 Wiki>http://dotnetdevelopmentinfrastructure.osscons.jp]]」は、「[[Open棟梁Project>https://github.com/OpenTouryoProject/]]」,「[[OSSコンソーシアム .NET開発基盤部会>https://www.osscons.jp/dotNetDevelopmentInfrastructure/]]」によって運営されています。 -戻る --[[React]] -> [[Redux]] --[[Reactのファースト・ステップ]] *目次 [#a4c3ea2d] #contents *概要 [#c8d6c3ab] create-react-appを使用した、[[React]] + [[Redux]]の step by step。 *手順1 [#fa5b6488] 色々悩んだが(と言うか、[[コレ>Reactのファースト・ステップ#n908d0bb]]の[[Redux]]化に失敗したため)、~ 先ずは、以下の「Example: Todo List」のClass化版をやることに。 -React+Redux で Todoアプリを作ってみる │ Web備忘録~ https://webbibouroku.com/Blog/Article/react-redux-todo -React Redux勝手にチュートリアル(TODO List) - TOEIC940点の文系プログラマー~ http://uraway.hatenablog.com/entry/2016/02/12/182048 ココでは、Web備忘録の方をやってみた。 **構成 [#s527e278] ─ src/ ├─ App.js ├─ index.js ├┬ actions/ │└─ Todo.js ├┬ components/ │└─ Todo.js ├┬ containers/ │└─ Todo.js ├┬ reducers/ │└─ Todo.js └── createStore.js **サンプルの生成 [#j3dbf15d] >create-react-app react-redux-todo **インストール [#maeb5c00] >npm install --save redux react-redux redux-logger **デバッグ設定 [#da9433e7] [[デバッグ設定>Reactのファースト・ステップ#v9b02d5b]]を行っておく。 **実装 [#idc1c700] 以下を見ても明らかだが、 >Entry Point, Component, Action, Reducer と、複数のモジュールを行き来してシンドい。 ***Store作成 [#ca8d8207] -createStore.jsを作成する。 -ポイント --combineReducersをココで書いている。 --applyMiddlewareで、~ Reduxの状態遷移をコンソール上に表示する~ redux-logger ミドルウェアを追加する。 ***Reducer作成(仮) [#h687216a] -reducers/Todo.jsを作成する。~ [[前述のStore>#ca8d8207]]でimportしているReducerを仮で作成。 ***[[Redux]]の組込 [#b10d5297] -index.jsに組込む。 --createStoreして、storeを生成する。 --AppタグをProviderタグでラップしつつstore属性にstoreを設定。 -ここで一度 npm start コマンドを実行し動作することを確認。 ***Component作成 [#ba4ac550] -components/Todo.jsを作成する。 -そして、App.jsを修正してTodoを呼び出す。 ***Action作成 [#v89cb346] -actions/Todo.jsを作成する。 ***Reducer修正 [#fd58d1c4] -Reducer(仮)のreducers/Todo.jsにActionに対応した処理を実装する。 -Actionから渡された値を「処理して」Componentに渡す。の処理の部分。 -ポイント --初期値はinitialStateで定義して使用する。 --state は書き換えるのではなく新たなオブジェクトとするので、~ Object.assignでディープコピーし、それに値を設定し、戻り値とする。 -- ***Container作成 [#r8f048de] -containers/Todo.jsを作成する。 -そして、App.jsを修正してTodoをComponentからContainerに切り替える。 -ポイント:ここでconnectする。 ***Component修正 [#mcd78a14] -components/Todo.jsで、propsのstateとfunctionを参照するように変更する。 **実行 [#sfd06e8d] -Todoアプリを実行して動作確認する。 -[[デバッグ実行>Reactのファースト・ステップ#v9b02d5b]]すると、redux-logger ミドルウェアで、~ [[Redux]]の状態遷移はコンソール上に表示される。 **サンプル [#z14db030] https://github.com/OpenTouryoProject/SampleProgram/tree/master/Template/SPATemplate/react-redux-todo *手順2 [#m993fb4d] 「Example: Todo List」が動作したので、[[先程>#fa5b6488]]失敗した[[コチラ>Reactのファースト・ステップ#n908d0bb]]の[[Redux]]化に再チャレンジ。 **プロジェクトの準備 [#c9c35b8c] -[[コチラ>Reactのファースト・ステップ#n908d0bb]]をコピーしてredux_appにリネーム。 -必要に応じて[[npmのインストール・コマンド>npm#ta17153d]]で~ node_modulesを復元する必要がある。 npm i **インストール [#t42a87de] >npm install --save redux react-redux redux-logger **デバッグ設定 [#ae168770] [[デバッグ設定>Reactのファースト・ステップ#v9b02d5b]]を行っておく。 **Component毎 [#tef34d40] ***実装 [#haacb1a8] 以下の辺りに注意しながら実装する。 -redux-loggerを使用 -createStoreの定義 -Actionの分割 -Reducerの書き方 -Containerの使用 -Componentからの参照方法 ***実行 [#bbb13e6f] -またも動かない。 -redux-loggerで、Reducersが動いていることは確認できるが、~ Component側が更新されない(State -> Componentの再実行がかかってない)。 -散々調べた挙げ句、原因は下で、 --combineReducersを使うと以下のようにStoreが階層化されるため、 {"Menu1":{"counter":0},"Menu2":{"counter":0}} --mapDispatchToPropsで、コレを考慮した設定にする必要があった。 const mapStateToProps = state => { return { counter: state.Menu1.counter } } -[[Redux]]さん、もっと、 --使い易さと、 --デバッグし易さを、 >考えて設計して欲しい感がある。 ***参考 [#tffeb8df] -combineReducersでハマったメモ~ https://qiita.com/usagi-f/items/ae568fb64c2eac882d05 -react-reduxで「dispatch is not a function」にハマった場合の対処法~ https://qiita.com/gaku3601/items/f77523bca6661f72f46a **Componentを跨ぐ [#gb1bc625] Componentを跨ぐという初歩的なことも良く解らないので、~ ネットを調べて以下の[[参考ページ>#t16b734d]]を発見したのでやってみる。 ***実装 [#h67e8391] Menu1、Menu2のContainerを集約するContainer, Action, Reducerを、Menu12に集約。 -Containerネストは不可能なので、Containerを集約する。 -従ってContainer以外にも、 Action, Reducerの集約を行う。 -親Containerから子Componentへは、propsを使用してstateとfunctionを渡す。 ***実行 [#xf51363b] 無事、動作! ***参考 [#t16b734d] -Stack Overflow --javascript - React & Redux : connect() to multiple components & best practices~ https://stackoverflow.com/questions/35032204/react-redux-connect-to-multiple-components-best-practices --reactjs - React Redux: More containers v.s. less containers~ https://stackoverflow.com/questions/39377712/react-redux-more-containers-v-s-less-containers -Qiita --何をreduxのコンテナにするか~ https://qiita.com/halhide/items/b9c80b69630ac89b8797 **サンプル [#t7bf1183] https://github.com/OpenTouryoProject/SampleProgram/tree/master/Template/SPATemplate/redux_app *手順3 [#s4a24c70] -[[コチラ>Reactのファースト・ステップ#gf8301c9]]の[[Redux]]化 -[[コチラ>https://techinfoofmicrosofttech.osscons.jp/index.php?ASP.NET%20Core%20React%2BRedux%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88]]の下記の新しい要素も追加する。 --ページング処理 --Hot Reloading --redux-thunkによる非同期処理 **プロジェクトの準備 [#f5f18271] -[[コチラ>Reactのファースト・ステップ#gf8301c9]]をコピーしてredux_templateにリネーム。 -必要に応じて[[npmのインストール・コマンド>npm#ta17153d]]で~ node_modulesを復元する必要がある。 npm i **デバッグ設定 [#t88ebb02] [[デバッグ設定>Reactのファースト・ステップ#v9b02d5b]]を行っておく。 **インストール [#maeb5c00] -既知のモジュール >npm install --save redux react-redux redux-logger -[[コチラ>https://techinfoofmicrosofttech.osscons.jp/index.php?ASP.NET%20Core%20React%2BRedux%E3%83%86%E3%83%B3%E3%83%97%E3%83%AC%E3%83%BC%E3%83%88]]の新しいモジュール >npm install --save redux-thunk react-router-redux node-noop history domain-task **実装 [#p848397e] -手順1の手順を参考に、本丸の[[Redux]]化に取り掛かる。 -Counter、FetchDataの順にモジュールを追加していくと良い。 ***Store作成 [#sdb092fa] -createStore.jsを作成する。 -ポイント --combineReducersをココで書いている。 --applyMiddlewareで、 ---Reduxの状態遷移をコンソール上に表示する redux-logger ミドルウェアを追加する。 ---非同期処理を行うための thunk ミドルウェアを追加する。 ***Reducer作成(仮) [#f322ef52] -仮のReducerを作成 -Counter、FetchDataの順にモジュールを追加 ***[[Redux]]の組込 [#l64ccec4] -index.jsに組込む。 --createStoreして、storeを生成する。 --AppContainerタグをProviderタグでラップしつつstore属性にstoreを設定。~ (以下のようなスニペットを発見したので、そのまま適用) ReactDOM.render( <Provider store={store}> <AppContainer /> </Provider>, ・・・ -また、routes.jsにページング処理のためのBrowserRouterを組込む。 --BrowserRouterのインポート import { BrowserRouter as Router, Route } from 'react-router-dom' --LayoutタグをBrowserRouterのタグで囲む(なお、?はnull許容のもよう) <Router> <Layout> ・・・ <Route path='/fetchdata/:startDateIndex?' component={ FetchData } /> </Layout> </Router> -ここで一度 npm start コマンドを実行し動作することを確認。 ***Component作成 [#sa661065] 作成済みなのでココでは何もしない。 ***Action作成 [#l4ebab9a] -Actionを作成 -Counter、FetchDataの順にモジュールを追加 -ポイント --Counterは通常通り。 --FetchDataは ---非同期処理自体をReducerではなくココに定義。 ---非同期処理の結果として、通常のActionを呼び出す。 ***Reducer修正 [#g6ecabce] -Reducer(仮)のreducers/Counter、FetchData.jsにActionに対応した処理を実装する。 -Actionから渡された値を「処理して」Componentに渡す。の処理の部分。 -ポイント --初期値はinitialStateで定義して使用する。 --state は書き換えるのではなく新たなオブジェクトとするので、~ Object.assignでディープコピーし、それに値を設定し、戻り値とする。 ***Container作成 [#gad2a8bd] -containers/Counter、FetchData.jsを作成する。 -そして、routes.jsを修正してCounter、FetchDataをComponentからContainerに切り替える。 -ポイント:ここでconnectする。 --Counterは通常通り。 --FetchDataはActionに定義した非同期処理を呼び出す。 ***Component修正 [#o4ef6ca6] -components/Counter、FetchData.jsで、propsのstateとfunctionを参照するように変更する。 -なお、componentWillReceivePropsは無限ループ(stack overflow)防止コードが必要だった。 **実行 [#vc6436a4] 無事、動作! **サンプル [#g981bbfd] -%%https://github.com/OpenTouryoProject/FrontendTemplates/tree/develop/Express %%(ASP.NETに移行したので削除された) -https://github.com/OpenTouryoProject/FrontendTemplates/tree/develop/SPA/React/redux_template **参考 [#q01ddb23] -BrowserRouterによるページング処理 --Access Route Params in React Router v4 | Jake Trent~ https://jaketrent.com/post/access-route-params-react-router-v4/ -redux-thunkによる非同期処理~ 注意:react-reduxを使っているか?いないか?で、サンプル・コードが大きく異なる。 --React + ReduxでREST APIを叩いてリスト表示する方法~ https://qiita.com/kazmaw/items/a2def8978127ffb11f92 *[[次の手順(サード・ステップ)>Reactのサード・ステップ]] [#z34624a2] *参考 [#v94b3eeb] **[[Reactのファースト・ステップ]] [#s8192f13]