React Native ~ Reduxの導入2 Componentとの紐付け~
containersとcomponents
どうも、兄です。
前回のRedux導入に引き続きComponent(View)となる箇所の紐付けを行っていきたいと思います。
前回も冒頭触れましたが、Viewの部分は以下のフォルダでカテゴリ分けするのが綺麗な分け方と記載しました。
- containers:モバイルではpageで設計してます。reduxとconnectし、actionをdispatchするところでもあります。
- components:ボタン、イメージ、リストビューなどの部品。containersからreduxの値を引数としてもらう
これはReduxとComponentの接地面をできるだけ抑えることで疎結合化を進めるのが目的だと考えています。また、責任の所在がはっきりするので、何かRedux関係でbugがあっても、あぁ、あのファイルを見ればいいのね。となります。
※ただ、containerは入り口で全てをComponentでpropsでやりとりすればいいかと言えば、それは違うと思ってて、作りやすい範囲でいいのではないかと思ってます。(いや、自分に言い聞かせてるのかw)
また、あくまでも、これはカテゴリ分けでcontainersもcomponentsもComponentでそれ以上でも以下でもありません。
まだ設計のベストプラクティクスを見つけれていないので、もっと勉強しなくちゃだし、実践あるのみだなぁと思ってます。
ComponentとReduxを紐付け
御託はここまでにして、Reduxとの紐付けを行います。
import React, { Component, PropTypes } from 'react'; import { Text, TextInput, TouchableOpacity, View, } from "react-native"; import { Actions } from 'react-native-router-flux'; import { connect } from 'react-redux'; class ToDoPage extends Component{ render(){ const {text} = this.props return( <View style={{flex:1, justifyContent:'center', alignItems:'center'}}> <Text style={{fontSize:24}}>{text}</Text> </View> ); } } // ① Stateとの受け皿となるObjectを定義 function mapStateToProps(state, props) { // reducerをstateから取得 const { todoReducer} = state // 必要なpropsを取得 const { text: text, } = todoReducer || { text: "", } // 返却(忘れるとComponentに渡らない) return { text, } } // ②受け皿とComponentを紐付け export default connect(mapStateToProps)(ToDoPage);
実は、ComponentとReduxを紐付けするのは、①と②の2つで、慣れてしまえば驚くほど簡単にReduxとの紐付けをすることができます。
更に、Component側ではpropsに自動的に入ってくるので、Reduxからなのか、親からの引数なのかは意識することなく使用することができます。疎結合の高さを垣間見ることができるでしょう。
それでは、確認してみます。
因みに、初期値がないとわかりにくいのでtodo-reducerを以下のように変更しています。
const initialState = { text: 'success connect component with redux', };
いい感じです!
ちゃんと、reducerの初期化処理で設定した、'success connect component with redux'が表示されていますね。
それでは、これを変更していきましょう。
ComponentからActionを行う。
アプリケーションのstate(状態)を変更したい場合は、必ずStoreに通知する必要があります。中でデータをいじるなら問題ありませんが、例えば画面遷移などで介し、状態を保持したい場合は必ずこのルールに従う必要があります。
その場合のエントリポイントはActionとなります。
必要なActionをimportします。
import { fetchTextUpdate, } from '../actions/todo-actions'
適当なイベントを作ります。
render(){ const {text} = this.props return( <View style={{flex:1, justifyContent:'center', alignItems:'center'}}> <Text style={{fontSize:24}}>{text}</Text> <TouchableOpacity style={{marginTop:20, padding:10, backgroundColor:'#ccc', borderRadius:4,}} onPress={this._onPressChangeText.bind(this)}> <Text style={{fontSize:24}}>State Change</Text> </TouchableOpacity> </View> ); }
actionをdispatchするイベントを作ります。dispatchはconnectすることで、propsとして渡ってきます。
_onPressChangeText(evt){ const {dispatch} = this.props dispatch(fetchTextUpdate('Changed Store')) }
いざ、実行!
できました!!!(おー:) パチパチ)
いかがでしょうか。
アプリケーションで保持する、Storeのstateなので、画面遷移をしても状態に変更がないことが確認できます。
また、Actions -> Reducer -> Store -> Component
とデータフローが一方行なのも確認できたと思います。
強制的にComponentはStateに合わせた結果をプログラムをするので良いので、へっぽこプログラマな僕みたなMVCのModelに引っ張られることなく実装ができて、とても魅力的なアーキテクチャだという印象です。
次回はもう少し深堀してみたいと思います。
それでわ。