React Nativeとは?

鎌倉正也(以下、鎌倉):それでは、始めさせていただきます。「フロントエンドエンジニアがReact Nativeを触ってみた」というタイトルで発表させていただきます。鎌倉と申します。

まずは、自己紹介なんですけれども。普段はフロントエンドエンジニアとして、ReactとかReduxとか使って、Webアプリを使っております。今年の1月くらいから、React Nativeの調査を始めまして、今日に至るという感じです。

そして今日のアジェンダですね。今日の内容は、React Nativeについてという話と、なぜReact Nativeを調査し始めたのかというのと、React Nativeのアーキテクチャの話。あとは、デモのアプリも作っていたので、その紹介を少ししようかなと思っています。

まずは、React Nativeについてなんですけど。ご存知の方もけっこういると思うんですが、Facebookが中心となって開発しているネイティブアプリのためのフレームワークです。特徴としては、Web技術、知識、HTML、CSS、JavaScript、Reactなどを使って、iOS、Androidのアプリを開発できます。

WebViewでなく、ネイティブのUIを直接JSがコントロールしているので、よって、ハイパフォーマンス。パフォーマンスがよくて、さらにReact Nativeが提供している機能でライブリロードとかホットリローディングというのがありまして、開発効率がいいという特徴があります。「ライブリロード」というのは、後でデモします。

もう1個、これは、Reactの思想なんですけど、「Learn once, Write anywhere」というのがありまして、「一度学んだら、どこでも書ける」みたいな言葉なんですけれども。今回のReact Nativeの例で言うと、WebでReactを触っていれば、React Nativeを使って、ネイティブアプリの開発もできます。

少しReactについて書いたんですけれども。最近、WebアプリでReactを採用するケースというのが増えていると思いまして、要は、Reactを書ける人材も増えている。リクルートでも、React、Reduxを使ったフロントエンド支援みたいなものがありまして、それによってReactを書ける人材も増えている。人員確保が楽になる。React Nativeを使うことによって、今までWebでReact書いてたエンジニアが、ほかの領域にも行けることによって、仕事幅を拡大できる。

今、React Nativeのプラットフォームの話が出たんですけども、iOS、Android、みなさんご存知だと思うんですけども、あと、tvOSもビルトインで開発することができます。

ほかにも、サードパーティ、ライブラリとか使うと、Windowsのアプリであったり、MacOSのデスクトップのアプリであったり。昨年発売された新しいMacBook Proのタッチバーを使ったMacのデスクトップアプリも作れたりします。

じゃあReact Nativeはどういうアプリで採用されているのかというところでショーケースを見てみると、FacebookやInstagram、Airbnbなど、けっこうユーザー数の多いアプリでも採用されています。

事例として、Instagramの事例が詳細に記事で上がっていたので、紹介しようと思います。Instagramは、もともとネイティブのアプリがあったので、既存アプリにReact Nativeを統合するような感じになりました。なので、まずは最初に簡単な画面から取り掛かっていったそうです。

もともとWebViewの画面で、UIがシンプルで、ナビゲーションも少ない、そんな簡単なシンプルな画面に改良してきました。Push Notification Setting、プッシュサービスの設定画面ですね。そういうものを経て、次に、ネイティブで作られたObjective-CとかJavaで作られた画面をReact Native化することを次に行っていて、パフォーマンスの測定とか、トレードオフがどれくらいあがるのかとか、そういうのをスイッチ化して、細かくパフォーマンス測定していったそうです。

結果、コードシェアリングの話なんですけども、これはReact Nativeといっても、iOS、Androidとまったく同じコードが使えるわけではなくて、それぞれに特化したコードを書く必要が出てきまして、それのコードシェアのパーセンテージが紹介されています。平均すると、だいたい90パーセントくらいは、iOS、Androidが共有できているような感じになっています。

そして、InstagramがWebViewからReact Nativeに書き換えたときによかった利点ですね。WebViewの問題点としては、起動が遅いだとか、UXにネイティブ感がないとか言われるんですけども、React Native化することによって、起動時間やUXが改善。もう1個、もともとネイティブで書かれていたところからReact Nativeに書き換えたことで、やってよかったのはコードの共通化ができたということ。

あとは、ライブリロードとかホットリローディングのおかげで、コンパイル、インストールのサイクルが早くなって、開発のパフォーマンスが向上したこと。これをやって、より早く機能をリリースするようなことができるようになりました。

ライブリロード、ホットリローディングの話が出てきてるので、軽くデモをしたいと思います。

これは、すごく簡単なカウントアップするだけのアプリなんですけども。こっちがコードですね。今、ちなみに、これはホットリロードが有効になっているような状態になっています。なので、例えばバックグラウンドカラーを書き換えて保存すると、このようにわざわざアプリをリロードしなくても、変更が通知されて書き換わるというようなことが可能になっています。これがホットリローディングですね。

もう1個、ライブリロードがあったので、そっちも見てみます。今度は、背景色を変えると……カウントの値がゼロにリセットされていることに注目してほしくて。今、ライブリロードだと、Stateの値がリセットされての再起動なので、すべてこれ、Redux以外で使っていたりするんですけれども、そのへんの値もStateの状態もInitial Stateに戻ります。

ホットリローディングのほうは、Stateの状態を保持したまま更新してくれるので、例えば、画面の階層が深いところに行って、ホットリローディングをやると、わざわざトップに戻らないで、そのページのままモジュールの更新ができるというようなことができます。

ReactとReact Nativeの違い

次は、Web版のReactとReact Nativeの違い。これは、コーディングの書き味とかの話になるんですけれども。

Viewは、コンポーネント、ライフスタイル等と考え方は同様で、要素は、React Nativeのコンポーネントに書き換える必要があります。スタイルのほうは、CSSの知識がそのまま使えます。使えるけど、単純にそう言い切れないこともあったので、あとで紹介します。Flexboxによって、コンポーネントの配置をしていきます。データフローはfluxアーキテクチャの利用がそのまま使えます。なので、Reduxを使って書いています。

ナビゲーション、画面遷移のところも、react-routerみたいなライブラリがありまして、それを使うことによって、ナビゲーションも実装可能になります。

1個1個説明していきます。Viewのほうは、JSXによる実装で、左が普通のWebのReactで、右側がReact Nativeになるんですけれども、それぞれReact Nativeの定義するコンポーネントに書き換える感じになります。さっきの画面で見てみると、このような感じ。HTML書いたことあれば、絶対わかると思います。

スタイルのほうなんですけれども、スタイルは、CSS in JSで書かれていて、オブジェクトでスタイルの指定をして、それを、StyleSheet.createで定義したスタイルをそれぞれのコンポーネントに適応していきます。定義したスタイルをコンポーネントのスタイル属性に当てはめていくような感じになります。

コンポーネントの配置は、Flexboxで指定します。flexDirection:’row’,とかってやると、横方向に要素を配置します。justifyContent:’space-between’,とかってやると、両端詰の等間隔で要素が配置するような、このようなかたちで画面を構築していきます。

いろいろ書いていて思うところがあって、CSS Modulesを使いたいと。自分もWeb使うときは、これを使っていたんですけれども、React Nativeでもできないかと思うところがあって。

そもそもCSS Modulesというのは、スタイル定義を別ファイルでCSSファイルとかにして、コンポーネントでインポートして利用する方法なんですけども、結局、React Nativeのスタイル定義というのは、CSSに似てるけど、やっぱり別物で、このような使い方は標準ではできません。

一応、実現できるパッケージはあるんですけども、できなくて、コンポーネントに付与できないプロパティがあると警告を出してくれる。このへんはわりと親切だなと思っているところですね。ほかの例だと、Viewコンポーネントにkeyで、カラーというのは対応できないので、このように警告が出ます。さらに、下にValid記述で有効な、利用可能なキーの名前などをリスト化してくれます。

次に、擬似要素。WebからReact Nativeで作ったので、Webで擬似要素とか使っている箇所が多くて思ったんですが、やはり擬似要素は諦めて普通に要素を書くしかないみたいです。Webフォントをbefore要素に入れて、というのを、Webではよくやると思うんですけども、そういうのができないので、リアルに要素を書くような感じになります。

似たような話で、擬似クラス。擬似クラスもJSで判定して、スタイルを指定する必要があります。React版Radium。Radiumも元々CSS in JSで擬似クラスとか、メディアクエリとか、そのへんを解決するためのライブラリだったんですけども、これのReact版みたいなもの場合は、できないこともないようです。

あとはshorthandの話がですが、shorthandも標準ではあまり使えなくて、それを解決しようとしているモジュールもあるんですけども、「う~ん」っていう(笑)。自分は、あまり使いませんでした。標準でもいくつか対応していて、marginとかpaddingとか、一応shorthandに近いものもあります。

CSSに慣れすぎると、このReact Nativeのスタイル定義っていうのは、最初、戸惑うことがあるかもしれません。今も示したように、外部モジュールで解決する選択肢というのもあるんですけど、React Nativeのバージョンが上がることによって、解決される問題も多々あると思います。

なので、まず標準でやって、どうしても自分が開発していて我慢ならないというときだけ、先ほど見せたような、サブに外部モジュールを使うような感じがいいかと思います。

iOSとAndroidの違い

次が、iOS、Androidの違いというところですね。「Write once, Run anywhere」、これは1回書けば、どのプラットフォームでも動くみたいな感じの話なんですけども、それは違います。

でも、プラットフォームごとにUI・UXのベストプラクティスというのも違ってくるので、React Nativeでも、それぞれ書く必要が出てくるところがあります。例えば、ナビゲーションとか、ハンバーガーメニュー、タブメニューとかというのは、プラットフォームごとにベストプラクティスが違ってくるので、そこは分けて書きます。

React Nativeで異なるコードを書く方法は、2つありまして。Platform.OS、Platform.select。あとは、ファイル自体を分ける方法。これだと、ファイル名の最後にiOS.jsとかAndroid.jsみたいな感じで、ファイル名を分ける方法があります。

Platform.selectプラットフォームのほうなんですけど、プラットフォームのほうは、(Platform.OS===’android’)だったら、アンドロイドのみで実行する……、実行されるほうですね。少し日本語おかしいですね(笑)。こういう感じになっています。下のPlatform.selectのほうは、オブジェクトで指定してあげて、キーがiOSだったら、Androidだったらみたいな処理をかけることができます。

ファイルを分ける方法は、このようなかたちで、使っているimport文のところを見てもらうと、exampleで指定してあげると、それぞれ、ビルド時にプラットフォームにあって、ビルド時にプラットフォームに適したexampleファイルを読み込むようなことができます。

なぜReact Nativeかというところで、Web技術を使った、Webモバイルアプリを作る、開発が可能になるフレームワークというのは、これ以外にもすごくたくさんあったんですけども。

React Nativeをなぜ選んだかというと、ユーザー数の多いアプリで採用されている。FacebookやInstagramだったり、Airbnbだったり。あと、リクルートテクノロジーズのフロントエンドの標準で、ReactやReduxというのを使っているので、Webで培った技術というのがそのまま使えます。コードも共有化が可能だったりするので、その恩恵も受けられます。FE標準で開発できるエンジニアも、今、リクルートで育てているので、よりメリットが大きくなってきます。

Webアプリを作ったあとに、スマホ版も欲しいという要望や需要が出てきたり。あとは、まだWebViewベースのサービスも多いので、React Native化によるパフォーマンスの向上が見込めます。最近は端末性能もよくなっているので、投資効果はアプリ特性に依存すると言っていたんですけども、まぁそうですね。

React Nativeのアーキテクチャ

次、React Nativeのアーキテクチャについて話します。すごくざっくりとした図なんですけども、ネイティブが動く部分と、ブリッジでやり取りしている部分と、JavaScriptは、JavaScriptCore(JSC)で動きます。

プロダクション環境は、JS Runtime。プロダクション環境では、JavaScriptCoreで動いて、ブリッジがIPCにあって、やっていたりします。デバッグモードの場合は、ブリッジにWebSocketを使って、データのやり取りをしていて、Chromeもデバッグモードで見れたりします。

Debug JS Remotelyというのをタップすると、こんな感じで、WebSocket、Chromeのデモツールを開いて、WebSocketのところを見ると、どういうデータをやり取りしているのかというのを見ることができます。さらに、Break Pointsも使うことができます。

次に、データフローのほう。先ほどのカウントアップのアプリで、Add+1っていうのをタッチしたときに、次に6になるまでのデータフローを見てみます。

タッチしたら、このようなJSのほうにEventEmitterがタッチされたよという情報、どの要素、ターゲットがタッチされたかというのを送ります。このなかで、Reactで動いて、setState()とかrender()とか、そういったことが実行されます。実行されると、次に、画面アップデートのような情報を教えてあげなくちゃいけないので、このようなかたちで、ViewIDとかattributesというのを渡します。

先ほどのカウントアップのアプリだと、ViewIDというのが8だったので、8と書いたんですけども、次、テキストが6になるので、6という情報を与えます。それをネイティブのほう、メインStateのほうでサブスクライブしていて、キューに入っているデータを見て、画面を更新するというような流れになります。

デモアプリを作ったんですけども、左がWebで右がReact Nativeという感じで、まだぜんぜんできていなかったりするので。今ちょっと手元で動かなくなってしまったのでデモはできないんですけど(笑)、同じような感じで作っています。

このデモアプリなどを作ってみての感想を、最後に述べます。WebのReactを使ったことがあれば、すぐにわかるし、書ける。さらにWebに近い感覚で開発もできます。もちろんnpmを使って、モジュールを読み込んだりというのはあるし、Cmd-Rでリロード、ライブリロード、ホットリローディングもWebと同じような感じで使えます。

今回、WebからReact Nativeに書き換えたんですけども、画面のほうは100パーセント作り直す必要がありました。先ほど言ったように、要素が全部違うので。ただ、Webで作ったコンポーネント、構造というのは参考にできます。Web版でFlexbox使っていればあまり考えることなくレイアウトを組むことができるんですけど、使っていなかったら考えることが増えてきます。

共通化できるコードも多かったです。今回、ReduxのActionsとかReducersとかっていうのは、丸々使うことができて、さらにBFFとの通信部分なども100パーセント共通化できています。

なので、mono-repo、1個のリポジトリ内でWebもスマホアプリも、みたいなこともフォルダ構成とか考えるのがちょっと面倒ですけど、できるんじゃないかなと踏んでいます。実際、Airbnbのほうではmono-repoでやってるようです。

気になるところで、Objective-CやJavaの知識が必要なところは今のところなくて。React Nativeが提供していないAPIを使いたいとか、あとは、CocoaPodsで導入したライブラリとジャバスクでブリッジしたいとか、そういうことが発生しなければ、Objective-CやJavaは、あまり気にならずに開発できてしまいます。

以上です。ご静聴ありがとうございました。

(会場拍手)