igniteで自分専用のボイラープレート環境を作る

この記事は React Native Advent Calendar 2017 16日目です。


こんにちは。Webエンジニアの@DotEarlです。

Togetterという会社で、自社アプリをReactNativeで開発しています。

目次

モチベーション

新しくReactNativeでアプリを作り始める時

最初にお決まりの構成、つまりボイラープレートを毎回作るのは面倒くさいと思います

例えばredux入れてmiddleware入れてデバッグツール組み込んで…など

出来ればinitでプロジェクト作ったら後は書くだけの環境が整って欲しいところ

igniteというコマンドラインツールを利用すると

自分専用のボイラープレート環境を

コマンド一発で生成することが出来るようになります

今回はその作り方を説明したいと思います

igniteとは

簡単に説明すると、react-native initする感覚でignite newすると

自分が設定したパッケージ/ディレクトリ構成と、react-native-xxxのようなネイティブモジュールを

入れるか入れないかyes/noで選びながらプロジェクトの初期化が出来るツールです

f:id:rskull:20171117012631g:plain

詳しくは以前に書いたのでこちらを

rskull.hateblo.jp

igniteでプロジェクトを始める時

新しいRNプロジェクトを作成する時は $ ignite new myApp の用に実行します

何も指定しないと公式自家製の環境がセレクトされますが

他に用意されたボイラープレートを使いたい時は

$ ignite new MyApp -b ir-boilerplate のように -b オプションで指定します

指定できるボイラープレート一覧はこちら

ignite/BOILERPLATES.md at master · infinitered/ignite · GitHub

この-bで指定するボイラープレートを今から自作していきます

前置きが長くなりました

自分用のボイラープレートを作る

時間がなくて適当になってしまいましたが

主に redux-sagaredux-actions を使った基盤を用意しました

GitHub - rskull/rn_redux_saga_sample

今から $ ignite new myApp した時にこの構成でプロジェクトが生成されるようにボイラープレートを作っていきます

1. 基盤を生成する

新しいボイラープレートを作る時は以下のようにコマンドを実行します

$ ignite plugin new ignite-sample-biilerplate

? Is this an app boilerplate plugin? -> Yes
? Will your plugin have an example component? -> No
? Will your plugin have a generator command?  -> Yes
Creating new plugin:  ignite-sample-biilerplate

$ cd ignite-sample-biilerplate
$ npm install
$ tree -L 1

├── README.md
├── boilerplate/ 
├── boilerplate.js
├── commands/
├── ignite.json
├── node_modules/
├── package-lock.json
├── package.json
├── plugin.js
├── templates/
└── test

まずはboilerplate/の中に入ってるignite/フォルダ以外を全て削除します

次に、用意したRNの環境からボイラープレートなJS部分を boilerplate/ フォルダにコピーします

このあたりは人によって構成が違うと思いますが、とりあえず上で貼ったRNのサンプルを元に説明します

ルートにあった index.jsindex.js.ejs に変更してboilerplate/にコピー

続いてそのファイルを開き、アプリ名が直書きされてる部分を書き換えます

// index.js.ejs
import { AppRegistry } from 'react-native';
import App from './src/App';

// $ ignite new myApp ←
// 生成した時のアプリ名が渡ってくるので、差し替わるようにする
AppRegistry.registerComponent('<%= props.name %>', () => App);

package.jsonpackage.json.ejsに名前を変更して、差し替わる部分があれば

同じように <%= props.name %> に書き換えます

今回はこれだけですが、他に.eslintや.flowconfigなどがあったら同じルールでコピーすればOKです

f:id:rskull:20171213000528p:plain

2. boilerplate.jsを書き換える

次はルートにあるboilerplate.jsを書き換えます

このファイルは、newした時に実行されるタスクの手順が書かれています

// boilerplate.js

// ...

// 単純に何をコピーするか指定するだけ
// 自分の環境にパスを合わせる
filesystem.copy(`${PLUGIN_PATH}/boilerplate/src`, `${APP_PATH}/src`, {
  overwrite: true
})
spinner.stop()

// ...

// 自分の環境に合わせて必要な設定を追加
const templates = [
  { template: 'index.js.ejs', target: 'index.js' },
  { template: 'package.json.ejs', target: 'package.json' },
  { template: 'ignite/ignite.json', target: 'ignite/ignite.json' }
]
await ignite.copyBatch(context, templates, { name: name }, {
  quiet: true,
  directory: `${PLUGIN_PATH}/boilerplate`
})
spinner.stop()

// ...

// 最初から入ってて不要なものがあれば消す
filesystem.remove('__tests__')
filesystem.remove('App.js')

// run npm install
spinner.text = '▸ installing ignite dependencies'

3. plugin.jsを書き換える

このファイルはreact-native-xxxxのようなnpmモジュールを適用させる設定を書きます

今回は追加してないので特に設定はありませんが、サンプルコードが書かれてるで不要な部分を消します

async function add (context) {
  // await screenExamples.add(context)
}

async function remove (context) {
  // await screenExamples.remove(context)
}

module.exports = { add, remove }

機会があればこのあたり深掘りする記事も書きたいと思います

4. 使ってみる

ひとまずこれで完成です!

このボラープレートを使ってプロジェクトを生成してみます!

適当なワークスペースに移動したら-bオプションで今作ったフォルダを指定して実行

$ ignite new myApp -b ./ignite-sample-biilerplate

これで用意した環境が適用されたプロジェクトが生成されたと思います

いつも通りreact-native run-iosなどして正常に動いていれば成功です

これで煩わしい環境構築を忘れて素早く開発がスタートできますね!!

ちなみに作成したボイラープレートはnpmに登録すると簡単に利用出来るようになります

ルールとしてパッケージ名はignite-のプリフィックスを付ける決まりです

指定するときはignite-を省略できます

今回はサンプルなので登録はしませんでしたが、もし登録してたら

どこにいても $ ignite new myApp -b sample-biilerplateで生成可能になります

今回つくったボイラープレートのリポジトリ

github.com

その他の機能

今回ふれなかった機能の紹介

igniteにはgenerateオプションがあります

例えば$ ignite generate reducer user の用に実行すると

reducer/フォルダにuser.jsテンプレートを作ってくれるなど

自分でコマンドをカスタマイズすることが出来ます

このエントリーでいじらなかったフォルダはこのあたりの設定を入れるやつです

templates/ -> generateで生成する元となるテンプレートファイル群を入れる

commands/ -> generateで何をどこにコピーするかなどの設定を入れる

詳しくは公式のボイラープレートのリポジトリを眺めてみて下さい

github.com

この辺りもいずれ書きたいと思います

感想

自分専用のボイラープレートを作って生成するところまでやってみました

generateオプションでファイルを自動生成するようにすればもっと開発効率上がりそう?

用意さえしてしまえば後々楽ができそうですね

以上で終わります

Happy boilerplating!

参考URL

ReactNativeにStorybookを導入するまで

storybookはコンポーネント単位でサンドボックス開発できる開発環境

どんなコンポーネントが存在するか、どんな種類があるか、使い方は?など

ドキュメントとしても機能するのであると便利

最近Webの方でよく使われてるようで、ReactNativeでも使える

導入

まずはstorybookをグローバルにインストールする

$ npm i -g @storybook/cli

RNのプロジェクトに移動してgetstorybookを実行

$ getstorybook

あとは勝手に設定されて

storybookというフォルダが作成される

最初からいくつかサンプルコードが入っているので実行して確認してみる

実行

ReactNativeの場合、コンポーネントの動作はシュミレーター上で確認するので

既にアプリが起動してる場合は、いつも動かしてる8081のサーバーは落としておく必要がある

まずはstorybookのサーバーを起動

$ yarn run storybook

localhost:7007にアクセスすると専用のページが表示される

この時点では、リストに何も表示されてなくてサンプルを確認出来ない

RNのアプリを立ち上げると、さっき起動したstorybookのサーバーに繋がって

アプリ側にも専用の画面が表示される

するとWebの画面にも用意したコンポーネントのリストが表示されるようになり

見たい項目をクリックすると、連動してシュミレーター上でも画面が切り替わる

これでやっと確認出来るようになった

※ 左の画面がiOSシュミレーター

f:id:rskull:20171126020353g:plain

最初からあるサンプルを参考に自分のアプリのコンポーネントも追加していけば

これでチーム開発も捗るのではないだろうか

今回動かしたのサンプル:GitHub - rskull/rn_storybook

でもシュミレーター上で確認しないといけないのはちょっと面倒くさいかも…

以上です

ミドルウェアを使わないreduxをちゃんと理解するための個人メモ

仕事でいきなりredux/redux-saga さらに redux-actions の環境を触ってきて

ミドルウェアを使わないシンプルな構成のreduxの理解が浅かったのでちゃんと覚えようと思った

環境構築

お手軽 create-react-app

github.com

$ create-react-app react-redux-example

次にreduxを使うのに必要なnpmモジュールをインストールする

$ yarn add redux react-redux

react-reduxreactredux を扱うために必要

これだけで環境は整った

処理の流れ

ActionType

ただの変数

イベントの名前を付けてく

const ADD_ITEM = 'ADD_ITEM'
const DELETE_ITEM = 'DELETE_ITEM'

Action

オブジェクトを返すだけのピュアな関数

typeを設定するのはお約束

// typeにActionTypeを設定
export const addItem = item => ({ type: ADD_ITEM, item })
export const deleteItem = id => ({ type: DELETE_ITEM, id })

// addItem('hello')
// -> { type: 'ADD_ITEM', item: 'hello' }

Reducer

storeの構造はここで決まる

import { combineReducers } from 'redux'

// 初期値
const initialState = [
  {
    item: 'hello'
  }
]

const todos = (state = initialState, action) => {
  switch (action.type) {

    // アイテムを追加
    case ADD_ITEM:
      return [...state, { item: action.item }]

    // アイテムを削除
    case DELETE_ITEM:
      return state.filter((v, k) => k !== action.id)

    default:
      return state
  }
}

// 分割されたreducerを合成
// これがstore全体の構造になる
const reducer = combineReducers({
  todos
})

Storeを生成

import { createStore } from 'redux'

// 前述のReducersで生成した値が入る
const store = createStore(reducer)

ミドルウェアがある場合はcreateStoreの第2引数に設定を渡すが

今回は素のredux構成なので省略

描画する

index.js を編集

import { Provider } from 'react-redux'
// ...

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

// ...

ここでやっとreact-reduxが登場

に生成したstoreを渡すと

storeの値やActionが使えるようになる

App.js

containerとなるものは全てこの形式で書かれる

import { connect } from 'react-redux'

class App extends Component<{}> {
  render() {
    const { todos, actions } = this.props
    return (
      <div>
        <button onClick={() => actions.addItem('world')}>ADD</button>
        <ul>
          {todos.map((v, k) => <li onClick={() => actions.deleteItem(k)}>{v.item}</li>)}
        </ul>
      </div>
    )
  }
}

// stateにstoreの値が入ってる
// this.props.todosで使えるようになる
const mapStateToProps = state => ({
  todos: state.todos
})

// Actionsで定義した関数が使えるようになる
// this.props.actions.addItem('hello')
const mapDispatchToProps = dispatch => ({
  actions: bindActionCreators(Actions, dispatch)
})

// Appコンポーネントに各値を接続
export default connect(mapStateToProps, mapDispatchToProps)(App)

f:id:rskull:20171122012742g:plain

まとめ

処理の流れが分かってスッキリした

今回のソース

わかりやすいように出来るだけ1ファイルに納めて書いてる

github.com

次はミドルウェアを導入してどう変わっていくかを確かめる

※追記

タイトル変えました

以上

igniteでモダンなReactNative環境を楽々セットアップ

f:id:rskull:20171117015447p:plain

react-native init appでアプリを始めても最初はreduxmiddlewareなど

いつも決まって使うような環境は入っていない

とりあえず試したいけど面倒くさいよ!って時はigniteコマンドを使うと

定番の環境、いわゆるボイラープレートをいい感じに構築してくれる

そうでない人も、個人的にフォルダ構成や書き方は参考になった

github.com

準備

react-native

まずはReactNativeを動かせる環境を作る

Getting Started - React Native

$ brew install node
$ brew install watchman
$ npm install -g react-native-cli

ignite

igniteをインストール

$ npm install -g ignite-cli

igniteコマンドを使うと

対話形式で定番のライブラリを導入するか聞かれていき

最後に全て設定が整ったプロジェクトが生成される

この生成されるボイラープレートには種類があり

下のリンクで配布されている中から -b で指定出来る

ignite/BOILERPLATES.md at master · infinitered/ignite · GitHub

また、自分でボイラープレートを作成することも出来る

ignite/creating-boilerplates.md at master · infinitered/ignite · GitHub

$ ignite new MyNewAppName -b ir-boilerplate

何もなも指定しないと、デフォルトで redux/redux-saga がメインの構成になる

試す

$ ignite new igniteApp

実行した後、いくつか質問されます

  • Would you like Ignite Development Screens?
    • 動いてるサンプルが見たい場合はyes
  • What vector icon library will you use?
    • 用意された様々なiconを表示出来るツールが使いたい場合はyes
  • What internationalization library will you use?
    • 多言語対応したい場合はyes
  • What animation library will you use?
    • 便利なアニメーション ライブラリが使いたい場合はyes

とりあえず全部yesにする

f:id:rskull:20171117012631g:plain

すると以下のようなフォルダが生成される

├── App
│   ├── Components
│   ├── Config
│   ├── Containers
│   ├── Fixtures
│   ├── I18n
│   ├── Images
│   ├── Lib
│   ├── Navigation
│   ├── Redux
│   ├── Sagas
│   ├── Services
│   ├── Themes
│   └── Transforms
│
├── README.md
├── Tests
├── android
├── app.json
├── ignite
├── index.js
├── ios
├── node_modules
├── package.json
├── storybook
└── yarn.lock

storybookもデフォルトで設定されてて嬉しい

起動

$ react-native run-ios

f:id:rskull:20171117014902g:plain

コンポーネントAPI・テーマ・画面遷移など、サンプルコードが一式確認出来るので

とりあえずコードを眺めたりいじったりしてみると良いのではないでしょうか

時間があったら自分用のボイラープレート作ってみる予定

以上