ついんてっく

双子のエンジニア(兄:web、弟:インフラ)が気になること、今やってるプロジェクトとかについて書きます。ぶっちゃけ1人でやって続かなかったから監視体制を置いたのが強いサボりん坊や

React Native ~ Reduxの導入2 Componentとの紐付け~

containersとcomponents

どうも、兄です。

前回のRedux導入に引き続きComponent(View)となる箇所の紐付けを行っていきたいと思います。

twins-tech.hatenablog.com

前回も冒頭触れましたが、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',
};

f:id:twins_tech:20160702181430g:plain

いい感じです!

ちゃんと、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'))
  }

いざ、実行!

f:id:twins_tech:20160702183717g:plain

できました!!!(おー:) パチパチ)

いかがでしょうか。

アプリケーションで保持する、Storeのstateなので、画面遷移をしても状態に変更がないことが確認できます。

また、Actions -> Reducer -> Store -> Componentとデータフローが一方行なのも確認できたと思います。

強制的にComponentはStateに合わせた結果をプログラムをするので良いので、へっぽこプログラマな僕みたなMVCのModelに引っ張られることなく実装ができて、とても魅力的なアーキテクチャだという印象です。

次回はもう少し深堀してみたいと思います。

それでわ。