Expo+Redux(+firebase)でログインフォーム③ 〜ファイル整理・Debugger〜


はじめに

Expo+Redux(+firebase)でログインフォーム② 〜Reduxの導入〜 の続きです。
この記事では、Ducksパターンを意識してReduxの各部品をファイルに分けていき、ReactNativeDebuggerを使えるようにするまでを扱います。

ファイル分割後のディレクトリ構成

root/
  ├ src/
      ├ App.tsx
      ├ store.ts
      ├ Styles.ts
      ├ components/
           ├ Counter.tsx
      ├ containers/
           ├ Counter.ts
      ├ modules/
           ├ index.ts
           ├ CounterModule.ts
  ├ node_modules/
  ├ package.json

ファイル分割

ほぼ、前回の記事のApp.tsxを分割しただけです。

App

src/App.tsx
import React from 'react';
import { Provider } from 'react-redux'
import store from './store'
import CounterContainer from './containers/Counter'

export default function App() {
  return (
    <Provider store={store}>
        <CounterContainer />
    </Provider>
  )
}

Store

src/store.ts
import { createStore, combineReducers } from 'redux'
import { counterModule } from './modules'

const store = createStore(
  combineReducers({ count: counterModule.counter })
)

export default store

Style

src/Styles.ts
import { StyleSheet } from 'react-native'

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#ecf0f1',
    padding: 8,
  },
  paragraph: {
    margin: 24,
    fontSize: 18,
    fontWeight: 'bold',
    textAlign: 'center',
  },
})

export default styles

これはなくてもいいですが、スタイルをつけなければ画面左上に要素が集まってしまい、見にくいですね。

Component

src/components/Counter.tsx
import React from 'react'
import { View, Text, Button } from 'react-native'
import styles from '../Styles'

const Counter = ({ count, increment, decrement }) => (
  <View style={styles.container}>
    <Text style={styles.paragraph}>{count}</Text>
    <Button
      title='Increment'
      onPress={increment}
    />
    <Button
      title='DECREMENT'
      onPress={decrement}
    />
  </View>
)

export default Counter

ReactのPresentational Componentです。
Buttonが押下された時に呼ばれる関数は下記のContainerに実装しておき、propsで渡すようにしています。ロジックはComponentには書かないほうがいいらしいです。

Container

src/containers/Counter.ts
import { connect } from 'react-redux'
import { counterModule } from '../modules'
import Counter from '../components/Counter'

const mapStateToProps = state => {
  return ({
    count: state.count
  })
}

const mapDispatchToProps = dispatch => {
  return ({
    increment: () => dispatch(counterModule.increment()),
    decrement: () => dispatch(counterModule.decrement())
  })
}

const CounterContainer = connect(mapStateToProps, mapDispatchToProps)(Counter)
export default CounterContainer

mapStateToProps, mapDispatchToPropsを定義して、connectを使ってComponentに渡しています。
今回は使わないですが、複雑なロジックが必要なら、これにmergePropsを加えるのがいいらしいです。

Module

src/modules/index.ts
import CounterModule from './CounterModule'

export const counterModule = new CounterModule()

moduleをまとめています。今後moduleが多くなっていくと、ここに追加していく感じです。

src/modules/CounterModule.ts
export default class CounterModule {
  // reducer
  counter(state, action) {
    if (action.type === 'undefined') {
      return null
    }

    switch(action.type) {
      case 'INCREMENT':
        return state + 1
      case 'DECREMENT':
        return state - 1
      default:
        return null
    }
  }

  // actionCreator
  increment = () => {
    return { type: 'INCREMENT' }
  }

  decrement = () => {
    return { type: 'DECREMENT' }
  }
}

actionTypeも作った方がいいんでしょうが、なんとなく省いています笑

React Native Debugger

このままではreduxがちゃんと動いているのか分かりにくいので、デバッガーを使いたいと思います。
こちらの記事を参考にさせてもらったのですが、そのままだと動かなかったので少し修正します。

Storeを修正

src/store.ts
import { createStore, combineReducers, compose, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { counterModule } from './modules'

const composeEnhancer = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

const store = createStore(
  combineReducers({ count: counterModule.counter }),
  composeEnhancer(applyMiddleware(thunk))
)

export default store

このようにミドルウェアのredux-thunkを挟まないと、デバッガが繋がらないみたいです(僕もよくわかっていない笑)

デバッガをインストール、起動

インストール

brew update && brew cask install react-native-debugger

起動

open "rndebugger://set-debugger-loc?host=localhost&port=19001"

はじめ、シングルクォーテーション(')で囲っていたのですが,なぜかエラー

The file /Users/atsushi/myProjects/react-navigation-redux/‘rndebugger:/set-debugger-loc?host=localhost does not exist.
[1]+  Exit 1                  open ‘rndebugger://set-debugger-loc?host=localhost
~

が出たので、ダブルクォーテーション(")で囲い直して見ると、うまくいきました。

次回

次回は、React Navigationを用いて画面遷移を実装していきます。
いよいよサインイン、サインアップ画面を作っていくことになります。