そーす

福岡在住。iOS/Androidアプリ, Webフロントエンドのエンジニアです。Swift, Kotlin, JavaScript, ReactNative

React Native version 0.47.0 is out

github.com

Breaking changes

Android

Remove unused createJSModules calls (ce6fb33, 53d5504) - @javache

ReactPackageというinterfaceからcreateJSModulesが削除されました。

これ結構ヤバイ変更だと思っています。

Androidのネイティブにアクセスするライブラリは軒並みこの変更に対応しないとビルド通りません。

ほぼすべてのライブラリで変更が必要なんじゃないかな

前バージョンではDeprecatedですらなかったのにこの変更は辛い。

(タグはv0.48.0-rcなんだけどな…)

自前のブリッジモジュールもそうですが、ライブラリの対応に追われますね…

Kotlinのrunとalsoの使い所

f:id:saburesan:20160906091652j:plain

twitterのタイムラインに

「runとalsoの使い所分からん」

というツイートが流れてきたので。

Kotlinには便利な拡張関数があるのですが、それぞれが微妙に違うので用途を結構迷います。

そこでrun, alsoに加えよく使うであろうletとapplyの4つの特徴から用途を考えていこうと思います

定義の確認

run

public inline fun <T, R> T.run(block: T.() -> R): R = block()
  • レシーバの拡張関数
  • 任意の型を返す
  • thisはレシーバ

let

public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
  • レシーバの拡張関数
  • 任意の型を返す
  • スコープ内外でthisが同じ

apply

public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
  • レシーバの拡張関数
  • 返り値はレシーバ
  • thisはレシーバ

also

public inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }
  • レシーバの拡張関数
  • 返り値はthis
  • スコープ内外でthisが同じ

違い

返り値がレシーバ(apply, also)

apply, alsoは返り値がレシーバに固定されます。 なのでレシーバの内部状態を変えるような処理(初期化など)が想定されます。

返り値が任意(run, let)

run, letは返り値がスコープ内の最後の処理の型になります。 つまり、レシーバを加工したい場合に使う事ができます。 加工はapply, alsoでは出来ない処理です。

スコープ内thisがレシーバ(run, apply)

レシーバ内のthisがレシーバに固定されます。 レシーバのメソッドを呼んだり、レシーバに対する処理を行うときは使いやすい。 反面、スコープ外のthisに参照したいときはthis@~~と各必要がある。

スコープ内外でthisが同じ(let, also)

tスコープ内のthisがスコープ外と同じです。thisが変わらないのでthis@~~と書く必要が無いので楽。 反面、レシーバはitなどで受け取らないといけない。 スコープ内でthisを多用するときは良いかも。

用途

上記を踏まえて私が思った使いどころ

let

ある値の加工に使う

val age = 10
val ageStr = age.let{ "age : $it" }

//Nullable時とかよく見るよね
age?.let { " age: $it" }

letはitでレシーバにthisでスコープ外にアクセスできて任意の型を返せるので、

レシーバの加工には一番適していると思います。

apply

初期化処理、メソッドの複数呼び出し

val human = Human().apply { 
   name = "sabure"
   age = "30"
   context = this@HogeActivity
}

applyはthisがレシーバでレシーバを返すので内部状態の変更・初期化処理などに向いているかと思います。

あとはthis@~が複数在るような場合はalsoだと混乱しそうなのでapplyが良いかと思います。

run

エルビス演算時のnullの時の処理?

val name = n ?: run {
   ...
   ...
}

KotlinのNullチェックはエルビス演算子を使うと思うのですが、

nullの時に幾つか処理をして返り値が必要な時などに使えるのではないでしょうか。

also

applyで処理するにはthisの参照が多そうなときかな…

val human = Human().also { 
   setOnClickA { this.startActivity(...) }
   setOnClickB { this.startActivity(...) }
   setOnClickC { this.startActivity(...) }
}

うーん、alsoはあんまり良い使い方思いつきませんね…

返り値がレシーバ固定なのでレシーバに関係ない事をするあまり良くないですし、

そう考えるとthisもレシーバのapplyの方がalsoに比べると使う機会が多そうな気がします。

まとめ

Kotlinの拡張関数は基本的に使ってて気持ちいいくらいなんですが、

それぞれの特徴を踏まえて用途を考えて使わないと、メンバー内などで書き方の統一が難しくなるので気をつけないといけませんね。

全部letでもいけるっちゃいけますからね。

「新しい技術の導入に関する勉強会」で発表してきました。

www.slideshare.net

SlideShareにアップロードしてます。

Keynoteで作ったのをPowerPointに変換して上げてるので若干レイアウト崩れてます(修正する元気無い)

React Nativeに関する簡単な紹介です。

福岡ではあまりReactやReact Nativeの話聞かないので少しでも知って貰えればなーという感じで作ったのですが、

結構Web,ネイティブアプリエンジニアじゃない方が多くて

「ReactとReact Nativeって違うんですか?」

「Viewって何?」

みたいな人もいたっぽいので若干内容ミスってました。

スライドも結構お粗末な感じになっちゃいました…

30分発表とか修論発表以来だわーー…反省

結構細かい所を濁しての発表で、マサカリチャンス結構あったのでビクビクしてました(笑)(ReactJSの説明のところとか)

どこまでの前提知識でスライド作るかって結構難しいですね。

発表は基本的に辛い話をしたほうが良い

私の発表を聞いた人は多分、誰かに「React Nativeってどんな感じだった?」って聞かれたら

「なんか辛そう…」

って言う人が居ると思います。

というかそういうふうに思ってもらうために発表の最後に辛い話をして、辛い話が記憶に残るようにしています。

新しめのフレームワークはやはり、新しいだけあって流行りの技術やニーズに対応した機能などが豊富です。

しかし、そのような良い特徴は公式がまず絶対紹介しています。

そりゃ使ってほしいから特徴や導入メリットなどはちゃんと書いてくれますよね。

しかし、公式は辛い話は書きません。

そりゃ使ってほしいからあえて辛い話とか書きませんよね。

私はその辛い話というのか一番の「知見」だと思っています。

辛い話は使ってみないとわからないです。

仮に、私の発表を聞いた人がReact Nativeの導入を考えた時に

公式が紹介する良い所と、

私が伝えた辛い所

両方を考慮して導入を検討してくれることを期待しています。

何はともあれ発表たのしい!またやりたい。

AndroidのカスタムOSが使われている端末でDevメニューが出ない時の対処

もー、クソん。

Oppo。

KeyEvent.KEYCODE_MENU(82番) がシステム側で受け取られててアプリまで来ない。

ちなみにOppoはシェイクしてもDevモード出ない。

しかたなく他の空いているキーコードをアプリで受け取って書き換えることに…

処理は簡単で、ReactActivity内でonKeyUp(int keyCode, KeyEvent event)をオーバーライドするだけ。

とりあえず300番代は空いていたので300を設定してみた。

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "MyApp";
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        MainApplication.getCallbackManager().onActivityResult(requestCode, resultCode, data);
    }

    // 追加
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        return super.onKeyUp(keyCode == 300 ? KeyEvent.KEYCODE_MENU : keyCode, event);
    }
}
> adb shell input keyevent 300

これでデバッグできる。

react-reduxのmapStateToPropsとmapDispatchToPropsは必要なのか

ReactNativeでアプリを作る時はReduxを使っていて、ReduxのStoreとReactのComponentをBindingしてくれるreact-reduxが便利なので使っています。

github.com

react-reduxが提供するconnectというStoreとComponentをBindingする関数が在るのですが、

connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps,
)(Component)

という感じで、3つの関数を引き数にしています。

mapStateToProps

これはReduxのStoreを第一引き数に取る関数で、ComponentにPropsとして渡すものをフィルタリングしたい時に使います。例えばStoreのUserからnameをPropsとして渡したい場合は

//第一引き数はstore
const mapStateToProps = store => ({ name: store.user.name });

connect(mapStateToProps)(Component);

//Componentはthis.props.nameでアクセス可能

こんな感じでフィルタリングしてComponentにPropsと渡すことが出来ます。

mapDispatchToProps

これはReduxのDispatchを第一引き数に取る関数で、変更を伝えるアクションを作成する時に使います。例えばボタンが押された時にUserのnameを変更するアクションを作成する場合は

// 第一引き数はReduxのdispatch関数
const mapDispatchToProps = dispatch => ({ updateName: name => dispatch({ type: UPDATE_NAME, name }) });

connect(null, mapDispatchToProps)(Component);

//Componentはthis.props.updateName('name')でアクセス可能

こんな感じで作成したアクションをComponentのPropsとして渡すことが出来ます。

mergeProps

これは第一引き数にmapStateToProps、第二引き数にmapDispatchToPropsをとる関数で新しPropsを作成します。 デフォルトでは

Object.assign({}, ownProps, stateProps, dispatchProps)

という処理を行います。

(ownPropsはComponentが持つプロパティです。mapStateToProps, mapDispatchToProps共に第二引き数, mergePropsは第三引き数で受取ることができます)

疑問

結論からいうとmapStateToPropsとmapDispatchToPropsの使い所がわかりません

例えば, Userの情報をサーバーから取得してStoreを更新する処理を考えます。

ユーザーの情報を取得するAPIにはユーザーのトークンが必要なのでStoreから取得する必要があります。

サーバーから取得したレスポンスでStoreを更新するためにDispatch関数が必要です。

なので、mergePropsを使わない場合は

const mapStateToProps = store => ({ token: store.user.token });

const mapDispatchToProps = dispatch => ({ fetchUser: token => fetch(...) }  });

connect(
  mapStateToProps,
  mapDispatchToProps,
)(Component);

のようになり、ComponentがfetchUserにトークンを渡す責務が発生します。

Componentは表示やUIイベントの通知に関わる処理だけをするべきであり、またtoken自体描画には全く関係無いのでPropsとして渡すのは不適切な気がします。

なので、ここでmergePropsを使ってtokenを渡す処理を内包すると

const mapStateToProps = store => ({ 
  token: store.user.token 
});

const mapDispatchToProps = dispatch => ({ 
  fetchUser: token => fetch(...), 
});

const mergeProps = (state, action) => ({
     fetchUser: () => action.fetchUser(state.token),
});

connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(Component);

これでも可能ですが、

なんなら

const mapStateToProps = store => store;

const mapDispatchToProps = dispatch => ({ dispatch });

const mergeProps = (store, { dispatch }) => ({
    fetchUser: () => {
        const token = store.user.token;
        fetch(...);
    }
});

connect(
  mapStateToProps,
  mapDispatchToProps,
  mergeProps
)(Component);

コレが一番シンプルなのでは??

という結論に今のところ至っており、

mapStateToPropsとmapDispatchToPropsは必要なのか

です。

だれか教えてください。よろしくお願いします!

「新しい技術導入に関する勉強会」に登壇します。

宣伝です。

「ReactNativeで始めるアプリ開発(仮)」という内容で登壇する予定です。

ReactNativeを触ったことがないWebエンジニア、アプリをエンジニアにわかりやすくメリット・デメリットを話せればと思います。

ReactNativeでアプリの開発環境(エディタ, ESLint, Flow編)

私がReactNativeを使ってアプリを開発するときの環境や初期設定です。

Visual Studio Code

Visual Studio Code - Visual Studio

1ヶ月前位にAtomから乗り換えました。

最初に変えた理由は何となくエディタを変えたかったので(笑)

とりあえず使ってみるとすごく快適に使えました。

Atomから乗り換えて一番よかったのは、エディタの設定です。

f:id:saburesan:20170602093805p:plain

atomはインストールしたパッケージの設定変更や、エディタ自体の設定変更が面倒(だと個人てきには思っていて)。

VSCodeは上記設定画面からエディタの設定とインストールしたパッケージの設定が行えます。

また、設定の変更が容易でわかりやすいです。

画面右がデフォルトから変更したユーザ個人の設定で、左が現在変更可能な設定一覧です。

例えば、よく使用するものfiles.autoSaveを変更したいとします。

f:id:saburesan:20170602093454p:plain

デフォルトではoffになっています。

他には何が設定できるかはfile.autoSaveはコメントに書いてあるのですが、

変更はカーソルを上にもってきた時に左に表示されるエンピツアイコンをクリックすると

f:id:saburesan:20170602094946p:plain

画像のように設定一覧だしてくれて選択出来ます。

これはめっちゃいいなーと思いました。

ESLint

eslint.org

ESLintはJavascriptの構文チェックツールです。

設定したコーディング規約に準拠していない場合はエラーを出してくれます。

Fileのパスの検証などもやってくれるので実行前に検出してくれます。

インストー

規約は自分で作るとかなり面倒なので、Airbnbが公開しているeslint-config-airbnbというのを使っています。

github.com

パッケージをインストールして

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

.eslintrcを作成して

{
  "extends": "airbnb"
}

を追記します。

エディタにPluginをインストー

次にエディタにeslintのプラグインをインストールします。

VSCodeでプラグインをインストールするには⌘+Ctrl+XでVisualStudioCodeのプラグインマーケットプレイスから検索ができます。

ESLintと打つと一番上に出てくるプラグインをインストールします。

f:id:saburesan:20170602104426p:plain

innsu

そうするとESLintのエラーをエディタ上で確認することが出来ます。

f:id:saburesan:20170602105129p:plain

エディタ上の赤い波線がESLintのエラー箇所になります。

画面下部のエラー一覧の画面は⌘+Ctrl+Mで出ます。

あとはエラー文言にそって修正していくだけです。

ReactNative用の設定を追記

eslint-config-airbnbにはReactNativeの設定が無いため追記します。

ReactNativeではXXX.ios.jsとXXX.android.jsをimport XXX from './XXX';という形式でインポートできるのですが、これはESLint上だとエラーになるので、.eslintrcにエラーが出ないように追記します。

{
  "extends": "airbnb",

  "settings": {
    "import/resolver": {
      "node": {
        "extensions": [
          ".js",
          ".ios.js",
          ".android.js"
        ]
      }
    }
  },
  "env": {
    "node": true
  }
}

settingsが追記分です。これでXXX.ios.jsのインポートエラーが解消されます。

Flow

flow.org

Flow_はFacebookが開発しているJavascript用の静的型チェックツールです。

プラグインのインストー

ReactNativeはデフォルトでFlowの設定がされているので、エディタのプラグインのインストールとflow-binのインストールだけで大丈夫です。

プラグインはESLintと同じように、

マーケットプレイスからFlow Language Supportをインストールします。

Flow Language Supportがグローバルにインストールされているflow-binを必要としますので、

npm i -g flow-bin

でインストールします。

これでVSCode上にもFlowのエラーが表示されるはずです。

エラー対処

おそらく、node_modulesの中もチェックされて大量にエラーが出てるかと思います…

そこで.flowconfignode_modulesをチェックしないように追記します。

[ignore]
.
.
.

.*/node_modules/.*

これでnode_modules以下のエラーは消えますが、react-nativeモジュールが見つからないエラーが出ます。

これはreact-nativeというモジュールをFlowが認識できるように定義することで対応できます。

.flowconfig[libs]というのがあるのですが、この[libs]に指定されているフォルダに定義を宣言することでFlowはその定義を元にチェックを行います。

React-Nativeのデフォルトでは

node_modules/react-native/Libraries/react-native/react-native-interface.js
node_modules/react-native/flow
flow/

上記のようになっているのでflowフォルダを作成して以下の内容のファイルを作成します。

declare module 'react-native' {
  declare module.exports: any;
}

この方法だとエラーはでなくなるのですが、これだと新しいモジュールの度に追加する必要があり、コードジャンプや補完が効かなくなります…

他の解決方法があれば知りたい…

おそらくmoduleでESLintのエラーがでるので、さらに.eslintrc

  "plugins": [
    "flowtype"
  ]

を追記します。

これでFlowの設定は一旦終わりです。

あとはflow/に定義ファイルを入れるとプロジェクト全体で定義のFlowチェックが効きます。

定義

// flow/Types.js
declare type Human = {
  name: string,
  age: number,
};

Twitter LiteはReactNativeWebで作られてる???

https://mobile.twitter.com/home

ちょっと前にTwitterLiteがリリースされました。

なんでもProgressive Web App(PWA)対応したとかで話題になってました。

はじめてのプログレッシブ ウェブアプリ  |  Web  |  Google Developers

気になってソースを少し見てみたのですが、なんだかReactNativeWebを使っているのでは??というソースを少し見かけました。

f:id:saburesan:20170531061527p:plain

写真のreact-native-xxxってやつですね。

JSのソース内にもreact_native_webという文字も有りました。

React Native Webとは

github.com

ReactNativeのソースをWeb用にも変換できるライブラリです。

まだ使ったことはないです。

んで、公式のサンプルを見てみるとスタイルの吐き出し方がTwitterLiteと同じでした。

公式サンプル https://www.webpackbin.com/bins/-KlQDQP2QfUqxXRQA8uc

なのでそのことに言及されてる記事とか無いかなと思ってしらべたら、

ReactNativeWebのオーナーが

「I’ll be sharing more about Twitter Lite (and React Native for Web) at React Europe on May 19th」

という発言をTwitterでしています。

このツイートは削除されていて、キャッシュが残っていたのでみれたのですが何か不都合があったかもしれないのでスクショは載せないでおきます。

調べるまで知らなかったのですが、ReactNativeWebのオーナーはTwitterの社員でTwitterLiteのTechnical Leaderだそうです。

うーん、可能性としてはありそうですよね…

ReactNative+ReactNavigation+Reduxで起きるバグ

2017/05/25時点です。

StackNavigatorのルートに他のNavigatorを入れるとエラーになる。

たとえば、

スプラッシュ→ログイン→メイン(ドロワー)

ってしたい時にメインまで行ったらメインでスタックを初期化したいですよね?

その時にメインがNavigatorだった時に起きます。

ただのReactComponentだと起きません。

Issue上がってます。

github.com

setParamsが効かない

辛い。

情報求む。

ReactNative+Redux環境で非同期アクションのテストを書く

Javascriptでちゃんとテスト書いてますか?

私は書いてないです。

テストはあんまり書いたことないです(ドン引き

Javascriptのテストは全く書いたことないです(ドンッ!!

今仕事で開発しているアプリはReactNativeなんですが、

やっぱスクリプト言語だと実行時までミスがわからないので怖いですね…。

もう怯えながらリリースするのは嫌なんだ…

というわけで、テストを導入してます。

ちなみにESLintflowは導入してます。

Jest

テストライブラリはJestを採用しました。

facebook.github.io

理由は、

  • ReactNativeは最初からJestの環境が整っている。
  • 構文がシンプルで簡単(テスト初心者でも使えそう。
  • Fluxの実装にReduxを使っているが、Jestでのテスト方法が公式にある。

という感じです。

プロジェクトを作る

とりあえず、新規RNプロジェクトを作って導入までをまとめます。

> react-native init redux_jest_sample

最初からJestがpackage.jsonに追加されていて、すぐにjestが動作する環境が整っています。 デフォルトで_test_にテストが書いてあるので実行してみます。

> npm test

> redux_jest_sample@0.0.1 test /Users/Ryohlan/Dev/react-native/redux_jest_sample
> jest

 PASS  __tests__/index.android.js
 PASS  __tests__/index.ios.js

Test Suites: 2 passed, 2 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.65s, estimated 10s
Ran all test suites.

テストがパスしました。

jestのテストは_test_ディレクトリ内か.text.js, .spec.jsが対象です。

通信のモック

nockというライブラリを使います。 github.com

nockは特定のリクエストが走ったときにその処理を横取りして任意のレスポンスを返してくれるライブラリです。

例えば、ReduxのHttp通信を行う非同期アクションのテストを行いたいときに、 通信のテストでは無いので特定の値を返してしまおうとういうものです。

 nock('http://hoge.com/api/v1')
        .get('/messages')
        .reply(200, { messages: ['hoge', 'fuga'] });

上記の例ではhttp://hoge.com/api/v1/messagesに対してGETメソッドでアクセスしたときに ステータスコード200で、レスポンスBodyを{ messages: ['hoge', 'fuga'] }で返すという書き方です。

ReduxのStoreのモック

redux-mock-storeというライブラリを使います github.com

非同期アクションが始まって終わるまでのアクションを保持してくれます。

非同期アクションのテスト

あとはアクションのテストを書くのみです。

//action
const REQUEST_MESSAGES = 'REQUEST_MESSAGES';
const SUCCESS_MESSAGES = 'SUCCESS_MESSAGES';
const requestMessges = () => ({ type: REQUEST_MESSAGES});
const successMessges = (messages) => ({ type: SUCCESS_MESSAGES , messages });
const fetchMessages = () => (dispatch) => {
 dispatch(requestMessges());
    return fetch('http://hoge.com/api/v1/messages')
               .then(response => response.json().then((json) => successMessages(json.message));
};


//reducer
const messages = (state = { messages: [], isFetching: false }, action )  => {
    switch(action.type) {
        case REQUEST_MESSAGES: 
            return Object.assign({}, state, { isFetching: true });
        case SUCCESS_MESSAGES: 
            return Object.assign({}, state, { messages: action.messages, isFetching: false };
        default: return state;
    }
};

//test
test('非同期テスト', () => {
    // Http通信のモック
  nock('http://hoge.com/api/v1')
        .get('/messages')
        .reply(200, { messages: ['hoge', 'fuga'] });

     const store = mockStore({}); // Reduxストアの初期化

      // ストアにアクションをディスパッチ
      return store.dispatch(Actions.fetchMessages())
        .then(() => expect(store.getActions()).toEqual(
          [
            { type: Actions.REQUEST_MESSAGES },
            { type: Actions.SUCCESS_MESSAGES, messages: ['hoge', 'fuga'] },
          ],
        ));
    });

重要なのは最後の

      return store.dispatch(Actions.fetchMessages())
        .then(() => expect(store.getActions()).toEqual(
          [
            { type: Actions.REQUEST_MESSAGES },
            { type: Actions.SUCCESS_MESSAGES, messages: ['hoge', 'fuga'] },
          ],
        ));
    });

です。

expect(A).toEqual(B)はAがBと同じであると言うJestのテスト構文です。

Actions.fetchMessages()でReduxの非同期アクションが始まります。

store.getActions()で非同期アクションの始まりから終わりまでのアクションが取得できるので、

期待されるアクションと比較することで非同期アクションのテストができるということです。

Javascriptの型チェック「Flow」まとめ

flow.org

個人的まとめ。 重要な所だけ読み返したい用です。 型名の和訳は適当です。 時間あったら追記する。

String

let text: string = 'hoge'; //OK
text = 'fuga'; // OK
text = 12;  // Error

let func = (value: string): string => value; // ok

Number

let num: number = 12; //OK
num = 12.3; //OK
num = 'hoge'; // Error

Boolean

let bool: boolean = false; //OK
bool = true; //OK
bool = 1223; //Error

Array

let array: Array<number> = [1, 3]; // OK
array = [3, 4, 4, 4]; // OK
array = ['hoge', 23]; // Error

Symbol

2017/05/18現在では未サポート

null, undefined

let hoge: null = null; // OK
hoge = undefined; // Error
hoge = 22e; // Error

let fuga: void = undefined; // OK
fuga = null; // Error
fuga = 124; // Error

Maybe型

KotlinとかSwiftとかのNullableとかOptionalとかですね

let maybe: ?string = 'text'; // OK
maybe = null; // OK
maybe = undefined; // OK
maybe = 123; // Error

maybe = `text ${maybe}`; // Error
if (maybe != null && maybe !== undefined) {
  maybe = `text ${maybe}`; // OK
}

Union

複数の型を許可します

let union: string | number = 'text'; // OK
union = 123; // OK
union = false; // Error

リテラル

特定の値のみ許可します。 ユニオン型と使うのが良さげ。Enum的な使い方かな?

let only2: 2 = 2; // OK
only2 = 3; // Error

let only2or3: 2 | 3 = 3; // OK
only2or3 = 3; // OK
only2or3 = 4; // Error

Another

i/oを統一したい時とかかな

const func = <T>(value: T): T => value;

Mixed

なんでもイケる。 サボりたい時(違うか

let mi: mixed = '23';
mi = false;
mi = 12;
mi = {};
mi = null;
mi = undefined;

関数のパラメータで使う時は注意が必要

let mFunc: func = (value: mixed) => (`text ${value}`); // Error

// OK
mFunc = (value: mixed) => {
  if (typeof value === 'string') {
    return `text ${value}`;
  }
  return value;
};

typeofなどで型をチェックした場合は以降はその型としてflowが型チェックを行う。 なので、以下はエラー。

mFunc = (value: mixed) => {
  if (typeof value === 'boolean') {
    return value + 'hoge'; // Error
  }
  return value;
};

詳しくは Type Refinements | Flow

Any

基本的に使っちゃ駄目なやつ。flowが完全に無視します。 mixedとの違いですが、

let mFunc: func = (value: any) => (`text ${value}`); // OK

先程mixedだった所を_anyに変えるとflowはエラーを出しません。 極力使用は避けましょう、との事。

関数


// OK
const f1: (value: string) => boolean = (value) => {
  return true;
};

// 実装ではパラメータ名が違っても良い
// OK
const f2: (value: string) => boolean = (v) => {
  return true;
};

// 変数の型はパラメータ無くても良い
// OK
const f3: (string) => boolean = (value) => {
  return true;
};

// 実装の方で型定義してもいい
// OK
const f4 = (value: string): boolean => {
  return true;
};

// どっちも書いても良い
// OK
const f5: (value: string) => boolean = (value: string): boolean => {
  return true;
};

// でも型は合わせないと駄目
// Error
const f6: (value: string) => boolean = (value: boolean): string => {
  return true;
};

// でも変数宣言のパラメータの型がMaybeじゃなくて実装のパラメータがMabyなら大丈夫っぽい。返り値はだめだった
// OK
const f7: (value: string) => boolean = (value: ?string): boolean => {
  return true;
};

// パラメータ名の後に?を付けるとオプションといして扱われる
// OK
const f8: (value?: string) => boolean = (value) => {
  console.log(value);
  return true;
};
f8(); // OK
f8(undefined); // OK
f8('hoge'); // OK
f8(null); // Error

Function

「関数である」ということだけをチェックする。なるべく利用は避けるべきとのこと。

const f9: (Function) => Function = (f: Function) => f(); // OK
f9(() => { })()()(); // OK

オブジェクト

const obj1: {
  foo: boolean,
  bar: ?string,
} =
  {
    foo: true,
    bar: null,
  };

obj1.foo = true; // OK
obj1.bar = 'bar'; // OK
obj1.piyo = null; // Error

厳密なオブジェクト

宣言したオブジェクト型以外のプロパティは許可しない。

const obj2: { foo: boolean, bar: ?string } = { foo: true, bar: null, bar2: 'hoge' }; // OK
const obj3: {|foo: boolean, bar: ?string|} = { foo: true, bar: null, bar2: 'hoge' }; // Error

タイプエイリアス

type NewType = {
  name: string,
  address: ?string,
  tel: number
}

const nType: NewType = {
  name: 'name',
  address: null,
  tel: 0,
};
nType.name = null; // Error
nType.name = 123; // Error
nType.hoge = ''; // Error


type Enum = 0 | 1 | 2;

let en: Enum = 2;
en = 0; // OK
en = 3; // Error

type State = 'state1' | 'state2' | 'state3';

let state: State = 'state1';
state = 'state3'; // OK
state = 'hoge'; // Error

interface

JavaのInterfaceっぽい。 デフォルトの実装は持てない。

interface Hello {
  name: string,
  say(): string
}

class HelloImpl {
  name = 'hogehoge'
  say() {
    return 'hello';
  }
}

class HelloImpl2 implements Hello {
  // nameとsayが実装してないとエラー
  name = 'fugafuga'
  say: () => ''
}

interface Address {
  address: string,
}

class HelloImpl3 implements Hello, Address {
  // nameとsayとaddressが実装してないとエラー
  name = 'fugafuga'
  address = ''
  say: () => ''
}

const h: Hello = new HelloImpl();
h.name
h.say();

const h2: Hello = new HelloImpl2();
h2.name;
h2.say();

const h3: Hello = new HelloImpl3();
h3.name;
h3.address; // Error

const h3_2: Address = new HelloImpl3();
h3_2.name; // Error

interfaceにはread/writeの制限がかけれる。 WriteOnlyの利点が理解出来ないので理解できたら追記する。

interface ReadOnly {
  +name: string
}

const ro: ReadOnly = { name: 'Ryohlan' };
ro.name; // Ok
ro.name = 'hoge'; // Error

interface WriteOnly {
  -name: string,
}

const wo: WriteOnly = { name: 'Ryohlan' };
wo.name = 'hoge'; // OK

ジェネリクス

interface Say<T> {
  name: T,
  say: () => T
}
const f: Say<string> = {
  name: 'hoge',
  say: () => 'hoge',
};
f.name;
f.say();
const f2: Say<number> = {
  name: 1, // OK
  say: () => 'hoge', // Error
};

class Item<T> {
  prop: T;

  constructor(param: T) {
    this.prop = param;
  }

  say(): T {
    return this.prop;
  }
}

const item: Item<string> = new Item('hoge'); /// OK
const item2: Item<string> = new Item(1); // Error


class Item<T: {name: string }> {
  prop: T;

  constructor(param: T) {
    this.prop = param;
  }

  sayName(): string {
    return this.prop.name; // OK
  }

  sayAddress(): string {
    return this.prop.address; // Error
  }
}

React Sketch.app で何ができるのか見てみた

f:id:saburesan:20170517103754p:plain

Introduction · react-sketchapp

ReactのシンタックスでSketchが動くらしい。

なんだかすごそう(小並感

インストー

公式のまんま

git clone https://github.com/airbnb/react-sketchapp.git
cd react-sketchapp/examples/basic-setup && npm install

basic-setupプロジェクトをエディタで開いてみると、

f:id:saburesan:20170517113237p:plain

おー、まんまReactですね。

package.jsonの中身は

{
  "name": "basic-setup",
  "version": "1.0.0",
  "description": "",
  "main": "basic-setup.sketchplugin",
  "manifest": "src/manifest.json",
  "scripts": {
    "build": "skpm build",
    "watch": "skpm build --watch",
    "render": "skpm build --watch --run",
    "render:once": "skpm build --run",
    "link-plugin": "skpm link"
  },
  "author": "Jon Gold <jon.gold@airbnb.com>",
  "license": "MIT",
  "devDependencies": {
    "skpm": "^0.9.0"
  },
  "dependencies": {
    "chroma-js": "^1.2.2",
    "prop-types": "^15.5.8",
    "react": "^15.4.2",
    "react-sketchapp": "^0.10.0",
    "react-test-renderer": "^15.4.2"
  }
}

ライブラリは特別なのはreact-sketchappくらいですね。Skech上でレンダリングできるコンポーネントライブラリでしょうか。

react-skechappの中身は

module.exports = {
  render: _render.render,
  renderToJSON: _render.renderToJSON,
  StyleSheet: _stylesheet2.default,
  Artboard: _Artboard2.default,
  Image: _Image2.default,
  RedBox: _RedBox2.default,
  Text: _Text2.default,
  TextStyles: _TextStyles2.default,
  View: _View2.default,
  Platform: _Platform2.default
};

使えるコンポーネントは多くはないようです。覚えることは少ないですね。

実行

> npm run render                                                      [master]

> basic-setup@1.0.0 render /Users/Ryohlan/dev/sketch-react/react-sketchapp/examples/basic-setup
> skpm build --watch --run

🖨  Copied src/manifest.json in 7ms


🔨  Built ./my-command.js in 2067ms

実行する前にsketchを立ち上げてプロジェクトを1つ開いておきましょう。 開かれてないと結果が描画されません。

f:id:saburesan:20170517113807p:plain

なるほど。

Hot Reaload動いてそうなのでコードを変更してみる。

f:id:saburesan:20170517114617p:plain

左上の色を黒に変更すると即座にSketchの方も変更されました。

まとめ

とりあえずちょっと触っただけ。 サンプルも結構あるので色々見てみます。

ReactNativeでJavascript側で起きたエラーを取得する

アプリのクラッシュレポートツールを大体入れているかと思うのですが、ReactNativeではどうするのが一般的なんでしょうかね。

FabricのCrashlyticsを入れたのですが、これはネイティブのエラーの箇所をレポートするので自前のネイティブ処理じゃない場合はライブラリ内のエラーの箇所が通知されます。

試しにApp.jsでエラーを投げてみると

f:id:saburesan:20170516161234p:plain

ワケガワカラナイヨ

これだと役に立たないですね…

というわけでJS側で起きるエラーを取得してそれを上手く通知できればいいなぁと思い調べてみました。

調べてみると、ErrorUtilsというオブジェクトがGlobalのプロパティに居るようで、こいつを使うとJS側のエラーをハンドリングできるようです。

github.com

試しにやってみました。一部だけ抜粋してます。

global.ErrorUtils.setGlobalHandler((e, isFatal) => {
  console.log(e);
});

export default class Sample extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Button title="Invoke Error" onPress={() => { throw new Error('test'); }} />
      </View>
    );
  }
}

Chromeのログだとこんな感じ

f:id:saburesan:20170516144300p:plain

log-iosのログだと

line: 64
column: 28,
sourceURL: '/index.ios.bundleplatform=ios&runModule=false&entryModuleOnly=true&hot=true'

取得できてるみたいですね。

リリースビルドだとどうでしょうか。

line: 64,
column: 28,
sourceURL: index.android.bundle

なるほど(?)

index.android.bundleがデフォルトだと無いようなので作ります。

> react-native bundle --platform android --dev false --entry-file index.android.js --bundle-output android/app/build/outputs/index.android.bundle

これでAndroidのリリース時のバンドルされたJSファイルが取得出来ます。

f:id:saburesan:20170516155830p:plain

指定の行、列を探してみたらちゃんとthrow Error(‘text’)したコードが見つかりました。

この情報を元にCrashlyticsでレポート送るとかやればいいのかな???

あと調べたら、JavascriptのクラッシュレポートツールにSentryっていうのがあるらしいんですが、これとかって結構使われてるんですかね? AirbnbとかUberとかでの導入実績が在るらしいですが…

sentry.io

20代の振り返り

明日、5月9日に私は30歳になる。

20代を終えると言うのは一つの大きな区切りのような気がする。

20代はいろんなことがあった。

東京で就職

大学院を出て24歳で初就職。

渋谷駅近くのモバイル系広告営業の会社に新卒エンジニアとして入り、Webのフロントコーディングを学ぶ。

といってもWebのフロントは全くやったことなかったけど誰も教えてくれなかったので結局覚えながら仕事をすることに。

半年くらいでその部署が解散して、次の部署ではガラケーサイトの更新業務をしてました。

iOSアプリできまーす」って言って内定出たと思ってたけどiOSアプリの申請くらいしかやらなかったなぁ。

11ヶ月で(色々あって)退職しました。

東京で起業

新卒入社半年くらいたって同期Aと同期Bの友達のフリーランスと業務外でサービスを作る機会があって、そいつが 3人で起業したい、って言い出した。 同期Aは新卒なのに(色々あって)窓際だったし、起業はしたいと思ってた(というか大学の時にも起業してた)らしい。

私はガラケーの更新業務してたしでどうしようかなーと思っていたんだけど、

多分私単独で起業しないからそういうのやりたいやつとやれる機会は無いかなーと思って退職を決意。

結婚

入社10ヶ月で大学時代から付き合っていた今の妻と結婚。

つまりは結婚して1ヶ月後に私は会社を辞めたのだ。

妻は若干怒ってた(相談はしなかった)

不妊治療

ふたりとも子供がほしいということで妊活することに。

が、中々子供ができず、たまたま近くに不妊治療で有名な産婦人科があったので検査しに行くことに。

すると私にも妻にもちょっと問題があり子供ができにくいという結果に。

ちなみに私は精子の量が普通の人の半分以下だった。

1年タイミング法で頑張ったが出来ず、人工授精をすることにした。

(人工授精をするために専用の容器にオナニーして精子を出すのだが、あれはなんとも言えない気持ちになる。)

私が通っていた産婦人科では人工授精は最大でも5回までだった。

人工授精はできる人は1回でできるが出来ない人は5回やっても出来ないらしい。

精子卵子自体に異常があるわけではなかったのですぐにできると思った。

が、人生そんなに甘く無いもので4回やって駄目だった(10万近くかかったかな)

体外受精が頭をよぎる。

あれめっちゃ高い。

色んな治療費で結構お金がかかったので、体外受精をするかどうかはちょっと考えないとな、という感じだった。

そんな心配をよそに、5回目の人工授精でめでたく成功し子供を授かることになった。

こういう経験があるので友達とかには子作りのときは出来ないなーと思ったが早めに産婦人科で検査を受けるように言ってる。

ちなみに私がそういう話をしたからって事で病院に行った友達が子宮筋腫が見つかり手術した。

発見が早かったからなのか手術後に子供を授かったようだ。

なので私は不妊治療をしたことは特に隠さずに人に話すようにしている。

私もそうだったようにまさか入院も骨折も捻挫すらしたことがない自分がとは思わない。

けど、周りの友達の話なら案外ちゃんと聞いてくれるし検査に行かないデメリットも特に無いしそれで友達が事なきを得たのであればなおさらだ。

コレを隠して最悪手遅れで子供出来ないとかなったら後味最悪だ。

というわけでみんなさっさと産婦人科行こう。

退職、福岡で起業

起業した会社は4人になったが、(色々あって)社長以外の3人は辞めて福岡で起業することに。

色々あった。

結婚してすぐに会社を辞め、妊娠して会社を辞め、こう見ると本当に駄目な人間である。

出産、立ち会い

12月17日の21時位だったと思う。妻が気分が悪いと言ってきたので陣痛かもしれないと。

とりあえずちょっと吐き気がするくらいだったので可能なら明日電話してみようということになったが、18日の深夜2時くらに大分つらそうだったので

産婦人科に電話してみると

「電話できるならまだ大丈夫ですね」

と言われ諦めることに。

しかしながらかなりつらそうだったのと、そもそも19日が予定日で18日は診察に行く予定だったのもあって病院に行くことに。

生まれそうなので入院してくださいとのことだったので入院。

そこから24時間妻は陣痛と戦いました。

そして私もその間ずっと腰擦ったり、公式テニスボールを妻のお尻の穴に当てたりして頑張りました。←これが楽になるらしい

日付変わって19日0時ごろ、妻が分娩室に入り私も立ち会うので一緒に入りました。

「あ”ぁ”あ”あ”あ”あ”あ”あ”あ”あ”あ”あ”あ”あ”あ”あ”あ”あ”」

ってずっと言ってて痛そうだった(小並感

そして

「オンギャーオンギャーオンギャーオンギャーオンギャーオンギャー」

めっちゃ(うるさい)声とともに生まれました。

そこの産婦人科ではへその緒を切らせてくれるので、せっかくなので切らせてもらいました。

なんかゴムホースを切ってる感覚に近くて、

1回で切れなくて3回くらいでようやく切れました。

出産は感動しますよ、立ち会えるなら立ち会ったほうがいいです。(感動の描写無いですが

20代の総評

良い20代でした。

結婚、出産を20代のうちにできたのはよかったです。

結婚して家族増えて、出産で家族増えて、めっちゃ楽しいです。

もう家族が居ない生活は考えられないですね。

苦労もありますが、それ以上に楽しいことが多いです。

ちなみに子育ては完全体力勝負なので若い内が良いです、マジで。

30代の抱負

あと子供を一人欲しい。

そしたらあとはひたすらに金を稼ぐだけ。

あと月1でアプリをリリースしたい。

まだ個人アプリは1つだけしか出してないのでもっと出して行きたいし、

本業以外での収入をもっと増やしていかないと駄目だなぁと。

さて30歳も頑張ろう。