ReactNativeでアプリを作る時の個人的な最小構成

React Nativeで開発をはじめる時の設定とディレクトリ構成をメモ。

一応個人的な構成としては、

- routerはreact-native-router-fluxを使う
- 状態管理にreduxを使う
- 非同期処理にredux-sagaを使う
- airbnbのスタイルをカスタムしてeslintを使う
- Flowで最低限の型は保証する

という感じ。

もしReduxいらない場合は、Redux関連のライブラリインストールとディレクトリ作成は省く。

ディレクトリ構成の例としてはこんな感じで。Redux使うバージョン。

```
index.ios.js
index.android.js
src
|__actions
   |__hoge.js
|__saga
   |__hoge.js
|__reducers
   |__index.js
   |__hogeReducer.js
|__components
   |__hogeScreen
     |__index.js
     |__list.js
   |__fooScreen
     |__index.js
     |__buttonA.js
     |__buttonB.js
|__constants
   |__hoge.js
|__libs // utility的な関数とかhelper的なやつ
|__models // strorageアクセスやAPIアクセス的なやつ
|__assets // stylesheetや画像とか
|__configureStore.js
```

特徴としては、redux用のディレクトリを作る以外にsaga用のディレクトを作るあたりか。

あとviewはcontaianerとかrouterで分けず、componentsにまとめることにする。

components内にページ単位のディレクトリを切って、その中にcontainer的なものとかボタンコンポーネントとか全部入れる。

開発環境はAtomを使っていて、プラグインは[このエントリ](http://qiita.com/hotchpotch/items/714a0e8f0d364282f95c)見ながら適宜。

ほんで実際に生成していく過程はざっくり以下。react-nativeとeslintはすでにインストールしている前提で。

# 1. init

```
react-native init ProjectName
cd ProjectName
```

# 2. ライブラリのインストール

eslint関連のインストール

```
(
  export PKG=eslint-config-airbnb;
  npm info "$PKG@latest" peerDependencies --json | command sed 's/[\{\},]//g ; s/: /@/g' | xargs npm install --save-dev "$PKG@latest"
)
```

reduxやその他routerのインストール。別にいらない場合はスキップで。

```
npm install --save prop-types react-redux redux redux-saga react-native-router-flux
```

# 3. eslintの設定ファイル作成

設定は適宜。

```
touch .eslintrc.js
vim .eslintrc.js
```

```[.eslintrc.js]
module.exports = {
    "extends": "airbnb",
    "plugins": [
        "react",
        "jsx-a11y",
        "import"
    ],
    "globals": { "fetch": false, "require": false },
    "rules": {
        "react/jsx-filename-extension": [
          1, {
            "extensions": [".js", ".jsx"]
          }
        ],
        "react/prefer-stateless-function": [
          0, { "ignorePureComponents": true}
        ],
        "react/forbid-prop-types": [
          0
        ],
        "no-console": [
          "error", { allow: ["log", "warn", "error"] }
        ],
        "react/no-array-index-key": [
          0
        ],
        "import/prefer-default-export": [
          0
        ],
        "no-constant-condition": [
          0
        ],
        "max-len": [
          0
        ],
        "no-underscore-dangle": [
          0
        ],
        "no-plusplus": [
          0
        ],
        "class-methods-use-this": [
          0
        ],
        "react/jsx-no-bind": [
          0
        ]
    }
};

```

# 4. ディレクトリ構成を作る

- Redux使う時

```
mkdir src src/actions src/reducers src/components src/constants src/saga src/models src/libs src/assets
touch ./src/configureStore.js
touch ./src/App.js
```

- Redux使わない時

```
mkdir src src/components src/constants src/models src/libs src/assets
touch ./src/App.js
```

# 5. 最低限のファイル作成

あとは適宜必要なファイルを作っていく。

Reduxを使う場合はファイル多いので省略。

下記はRedux使わない場合。

```index.ios.js
/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React from 'react';
import {
  AppRegistry,
} from 'react-native';

import App from './src/App';

const SampleApp = () => (
  <App />
);

AppRegistry.registerComponent('SampleApp', () => SampleApp);
```

```./src/App.js
import React from 'react';
import { Router, Scene } from 'react-native-router-flux';
import Root from './components/RootScreen/index';

const App = () => (
  <Router>
    <Scene key="root">
      <Scene key="home" component={Root} title="SampleApp" initial />
    </Scene>
  </Router>
);

export default App;
```

```src/components/RootScreen/index.js
import React from 'react';
import {
  View,
  Text,
} from 'react-native';

class Root extends React.Component {
  render() {
    return (
      <View>
        <Text>Test</Text>
      </View>
    );
  }
}

export default Root;
```

<img width="375" alt="Simulator Screen Shot 2017.08.11 10.03.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/13609/8d1dc268-f2b4-125c-e2ff-ca499b49e8fd.png">


# 6. FlowTypedの設定

デフォルトで設定されてるからほとんどやることはないんだけど、ひとつだけreact-nativeからパッケージをimportすると

```
react-native Required module not found
```

のエラーが出てしまう問題を解決する。

方法は、当該のパッケージの型をdeclareで定義したファイルを作成し、.flowconfigで読み込ませること。

詳しい設定は下記を参照して。

- [React Native+Flowにおける.flowconfigの設定方法](https://qiita.com/gyarasu/items/0f0be114490981bd8cab#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95)

# サンプル

この構成に習ってreact-native + redux-sagaでストップウォッチのサンプルアプリを作っておいたのでみてもらうとわかりやすいと思われ。

[YuheiNakasaka/react-native-redux-saga-stopwatch](https://github.com/YuheiNakasaka/react-native-redux-saga-stopwatch)

# 参考リンク

- [redux-saga](https://redux-saga.js.org/)
- [ReactNavite 開発に Atom を使う](http://qiita.com/hotchpotch/items/714a0e8f0d364282f95c)