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

# はじめに
[Expo+Redux(+firebase)でログインフォーム②　〜Reduxの導入〜](https://qiita.com/AtsushiNi/items/57851128a32020d2e1b8#%E6%AC%A1%E5%9B%9E)　の続きです。
この記事では、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
```
# ファイル分割
ほぼ、[前回の記事](https://qiita.com/AtsushiNi/items/57851128a32020d2e1b8#%E6%AC%A1%E5%9B%9E)のApp.tsxを分割しただけです。

### App
```Javascript: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
```Javascript:src/store.ts
import { createStore, combineReducers } from 'redux'
import { counterModule } from './modules'

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

export default store
```

### Style
```Javascript: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
```Javascript: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
```Javascript: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
```Javascript:src/modules/index.ts
import CounterModule from './CounterModule'

export const counterModule = new CounterModule()
```
moduleをまとめています。今後moduleが多くなっていくと、ここに追加していく感じです。


```Javascript: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がちゃんと動いているのか分かりにくいので、デバッガーを使いたいと思います。
[こちらの記事](https://qiita.com/Tetsuya3850/items/e8d4450fcb3df5522b7a)を参考にさせてもらったのですが、そのままだと動かなかったので少し修正します。

### Storeを修正
```Javascript: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
~
```
が出たので、ダブルクォーテーション(")で囲い直して見ると、うまくいきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549051/9e579863-5378-4530-1540-eca15b37e764.png)

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