普段思ったことや、雑記。

Menu & Search

React Native開発のつらい点まとめ

2017年5月14日
React

React Native v0.42で開発していて、つらい点を述べていく。良い点はあったかもしれないが、忘れてしまった…(良い点含めたより公平な意見は、あらためてまた今度書く)。なお、製品でReact Nativeを運用されている方で、他にもつらいとおっしゃっている方もいるようなので、自分がReact Nativeに対して感じているこのつらさは間違っていないと思う。

大前提として「React Nativeは、Viewしか扱わないReactがベース」である

これがそもそものReact Nativeがつらいと思える根本的な原因かもしれない。React Nativeのコンセプト通り、React Nativeではたしかに、Reactの知見をほとんどそのまま流用してReact Nativeではアプリケーションを開発することができる。しかし、その知見のもととなる本家ReactはViewしか取扱いをせず、HTTP通信だったり、ルーティングだったりはサポートしない。よって、これらをするために、React Nativeでは、HTTP通信(2017年8月30日追記、9月11日更新: fetchというAPIがReact NativeによりJavascript標準で提供されているので、これを使ったりする。他、パッケージを使う方法だとaxiosなどを使う)、ルーターと、React以外に他のパッケージを読み込み、View以外に必要な機能を追加していくことになる。また、Reactのライフサイクルメソッドはシンプルで、画面コンポーネントがマウントされそうとともにライフサイクルが開始し、最初のライフサイクルメソッドがフックされ、画面がアンマウントされそうになると最後のライフサイクルメソッドが呼ばれる。ここで問題なのが基本動作がこのライフサイクル、画面と、画面内のコンポーネント描画のタイミングに縛られるということだ(Reactのライフサイクルメソッドはこちら→ https://facebook.github.io/react/docs/react-component.html)。Webより複雑なイベントハンドリング、コントロールを求められるクライアントアプリケーションともなると、Webではそれで解決していた部分でも、いわゆる「かゆいところに手がとどかない」状態でやりづらく感じることがある。

つらい点一覧

だいたい言いたいことは言ったのだが(いきなりReactをクライアントアプリに使おうとするのが悪い、みたいな感じで極論すぎるが…)、細かいことについても下記に書いていく。

ことあるごとに、パッケージを追加していく開発スタイル

冒頭でも述べたが、Reactでは通信部など、足りない部分はパッケージを追加して機能を補う。React Nativeでは、React Native用のコンポーネントが公開されている。よって、ちょっと凝った機能を持ったコンポーネントはOSSとして公開されているコンポーネントを使用するシーンが多い。例えば、小さなあたりで言うと、画面全体をロックしたい場合はちょっと工夫が必要で、これは用途にもよるが react-native-loading-spinner-overlay を使ったり、大きなあたりでいうと、チャット機能を実装したいとなれば、react-native-gifted-chatを使ったりする。あとは、UIの補助にはUIコンポーネントがまとまったNativeBaseを使うこともある。要は、そもそもReact Native上で動かすReactで足りない機能をパッケージ追加、React Nativeで車輪の再発明をしたくない場合はパッケージ追加(コンポーネント以外もあるが、コンポーネントが多い)、とReact、React Nativeとの両者で補っていく。こうしていくと、該当のReact Nativeのバージョンで動いているうちは良いのだが、開発中は結構盛り盛りでこういったパッケージを入れていくので、どのパッケージがメンテナンスがされなくなっただとか、ロックインの発生が今後気になるところだ。

WebでのReactのライフサイクルメソッドがネイティブアプリにマッチしているとはいえない

冒頭で述べたように、React Nativeの処理はReactコンポーネントのライフサイクルに縛られるため、柔軟性に乏しい。Webよりクライアントアプリケーションのほうが繊細な挙動ができるため、もう少しクライアントアプリ向けのイベントが欲しい気がする(例えば画面が切り替わる際にコンポーネントを破棄するか生かすか選べるとか。そもそもReactは管轄外だから積んでるのはわかっているけど…)。あくまでViewのライフサイクルにロジックを紐付ける感じになるので、どうしてもWebっぽい作りになる。ゆえに、React Nativeは画面をまたいだ場合の処理というのが結構苦手だ。

よく使われる定番のパッケージすら安定していない・ハマる

react-native-router-fluxはよく使われるルーターだが、動作が安定しないことがある。また、NativeBaseはよく使われるUIライブラリだが、デザインを調整しようとなると、最初導入した際には良い具合な見た目だったものの、調整段階で結局は外したほうがマシと、よかれと思って導入した後にハマったり、箇所によっては調整に時間を使うことがあった。

2017年8月21日追記:

react-native-router-fluxに変わり、ルーターはreact communityのreact-navigationに主流が変わりそうだ。react-native-router-fluxの次期バージョンもベースはreact-navigationにすると言っているし。これを諸行無常と言わず、何と言うか。本当にありがとうございました。

新しめな資料は英語しかない

React Nativeの日本語資料は現行の開発で参考にした機会はなかったように思える。日本でもReact Nativeを採用して開発している企業はまだ少ないようなので当然だと思うが、資料が英語のみとなった場合での調査コストの増加は技術選定時には見逃せない点である。英語は読めばいいじゃんという辛辣な意見を聞いたような気がするが、読んでやってみたけど日本語ドキュメントがあるほうが日本語の経験が長いので圧倒的に理解が早いのではないかという認識(=開発速度も日本語ドキュメントが豊富なほうが、日本人が集まった場合早いのでは)。

気がついたらGithubのIssueを読んでいる。気がついたらソースの中身を読んでいる。気がついたら1〜2年前のStack Overflowをじっくり読んでいる

要するに、React Nativeと非公式パッケージのドキュメントが貧弱ということだ。React Native公式では、標準コンポーネントで使えるオプションだったり、サンプルコードを解説こそしてあるが、かなりあっさりしている。また、非公式のパッケージはコンポーネントで使えるオプションこそ列挙してあるが、使い方はソースコード読んでね!とか平気で書いてあるので、ドキュメントについては期待できない。

※2017年6月12日 Androidでの動作状況について、訂正内容

Android端末3機種(ファーウェイ製最新端末、ASUS製最新端末、ZTE製ミドルレンジ端末)でリリースビルドの検証を行ったところ、速度面ではハードウェア性能に依存し、最新端末ではiOS(iPhone 6S)と遜色がない程度の挙動であった。ただ、アニメーション時のフレームレートについて、厳密には計測してないものの品質面ではiOSに軍配が上がっているという認識でいる。また、Androidでのコンポーネントの不備について、実装上の問題であったり、iOSベースで開発をしてきた結果、Android端末で検証不足のため、コンポーネントのスタイリングが甘いことによりこういう事になったことが分かったので、訂正する。ただ、React Native開発のだいたいのケースでこのようにiOSとAndroidの差異に悩むケースはどのタイミングかで遭遇しえると思う。そしてコンポーネントのスタイリングを、Flexboxベースでしっかりやることでだいたいのパターンが解消されると思う。

以上により、下記の指摘内容は検証が甘く、AndroidでのReact Nativeのパフォーマンスについて語弊を与える可能性が高いので取り消し線にて訂正をした。取り消し線箇所のうち、Android実機かつデバッグモードの動作条件だと、挙動が極めて重たいのは言うまでもないので、一応ここだけは抽出して強調しておく。

iOSはわりとしっかり動くが、Androidでは挙動が遅かったり、あやしいことが度々ある

React NativeはiOSのために作られたのかと思うほど、iOSでは思ったように動く。しかし、Androidでは最新機種をもってしてもリリースビルドでもモッサリしているし、デバッグモードで実行ともなるとその動作の重さがより際立つことになる(デバッグモードにかぎっては、後述するがデバッガの品質の問題もある)。また、Androidではちょっとした動作の不備が散見できた。例えばコンポーネントがiOSシミュレータでは見えていたが、Android端末では見えない、などの事態があった。

デバッガの動作が不安定だし、重い

React Nativeのデバッガ(※正確には、React Nativeのpackager、デバッガの役割も果たしていて、一般的に分かりやすそうなので)の出来は相当悪いと思う(2017年5月23日追記:フロントエンドには、react-native-debaggerを使用していた)。React Nativeをデバッグモードで実行していると、iOSシミュレータのリロード中に接続不能になって、ビルドから始めることも結構ある(8081で起動しているデバッガから応答がないだとか、そんな感じのエラーメッセージが出てデバッグ中アプリが動かなくなる)。個人的にデバッガのツボな挙動でわりと面白いのは、画像リソースをデバッガ起動中にプロジェクトディレクトリへぶち込んでみると、デバッガが即クラッシュしてアプリの動作を完全に停止させることができることだ(画像リソースなんて任意のタイミングで追加できていいじゃないか…とこれは毎度思う。案外よくやってしまうので、その都度デバッガを再起動する)。iOSではシミュレータ、実機ともにデバッグ時のアプリケーションの速度はストレスというほどでもないが、Androidの実機デバッグではデバッグ時の重さが際立つ。そのため、Androidの長めのテストは、リリースビルドをかけてやることが多かった。

React Nativeで解決できない箇所は、React Nativeが吐き出したネイティヴ向けのコードをメンテナンスすれば良いと思っていたら、難易度高そうで諦めた

React Nativeについてよく調べずに使っていたというのがそもそもの失敗だったのだが、Androidを例にすると、React Native上で画面が複数記述してあったとしても、Androidでは画面が1つだけ、ファイルでいうとMainActivity1つしか生成されない。実際のコードを見てみて気づいたのだが、MainActivity内でコンポーネントを書き換えを延々しているようで、例えば「ここの画面のテキストフォームはAndoroidだと表示崩れを起こしているから、React Nativeが生成したAndroidのネイティブコードを操作しよう」ということが非常にやりにくいということだ(2017年6月15日修正、例えがあまり良くないので削除)。もしかしたらうまいことできる方法があるのかもしれないが、Androidのコード郡をパッと見た感じが不明だったので、今のところは時間的余裕がない限りは積極的にはやらないほうが良さそうと結論付けている。

コンポーネント志向とか言いながら、オリジナルのコンポーネントの再利用が起きなくて、コンポーネントのメリットをあまり享受できた感じがしない

これはアプリケーションの規模感にもよるかもしれない。React Nativeでオリジナルのコンポーネントを作ったとして、再利用する機会がほとんどなかった。コンポーネント化の目的が再利用というよりかは、画面の肥大化を防いだり、コンポーネントに分けることで適切にスコープを設定するために再利用可能なコンポーネントとして定義していた。しかしながら、Reactコンポーネントは、インタフェースが統一になるので、OSSのコンポーネントを利用する際にはコンポーネントの仕組みが非常に役に立ったと思う。

GUIでUIを編集できるツールがない・これというオンリーワンなIDEがない

ReactのコミュニティはJavaScript界隈の中では、かなり大きいコミュニティだと思う。これだけ流行っているような感じなのだが、まだ発展途上のようで、React関係のIDEはいくつかあるものの、これを使えば間違いないというものは現状存在しない。よって、開発ともなると、皆思い思いのエディタでコードを書いているのが現状だ。IDEがないことにより、ネイティブアプリ開発者にはもはや当然となっている、IDE付属のレイアウトエディタによってUIパーツの位置決めをしていくという当然のこともReact Nativeでは不能である。Reactコミュニティは急速に発展しているのと、あとはReact Native自体がまだv1.0に達していないことから(2017年5月14日現在)、今後IDEにまつわる問題は解消するかもしれない、…が、そもそもそReact自体がWeb発の技術なためにあまり期待しないことにする。Web開発者がReact Nativeを扱い出すとこの点はあまり気にならない、むしろ今まで通りにWebのReact開発でそうであったように、JSXタグを打ち込んでいくので慣れている感すら感じるかもしれない。一方で、XcodeやAndroid Studio、UnityなどのクライアントアプリのIDE使用経験者にとっては、標準で使用可能なUIアセットを閲覧できないこと、GUIによる大まかな位置決めすらできないことなど、色々とストレスを感じる環境かもしれない。

アプリケーションのパフォーマンスは各端末上で動作するJavaScriptインタプリタの性能頼み

React NativeはiOS端末だと本当によく動くが、一方でAndroid端末では最近の端末ですらリリースビルドでもモッサリした感じになる(2017年6月12日訂正。検証不足による記述)。React Nativeの動作は、端末のハードウェア性能、該当OSのJavaScriptインタプリタの性能に依存するので、特に機種によって性能差やOSバージョンの差異が激しいAndroidでは動作検証に注意が必要だ。最新端末とそうでないものだと、速度面でかなりのギャップを感じられる(2017年6月12日追記)。

画面切り替えをすると、表示中コンポーネントがアンマウントされる。新しい画面が出現するとコンポーネントがマウントされる。画面をアンマウントされなくて良く、バックグラウンドで動作するという選択肢はなく、画面の状態・処理状態を任意に保持・破棄することができない

React Nativeは普通に組んでいくとどうしても作りがWebっぽくなってしまう。理由は簡単で、WebのReactのライフサイクルメソッドがそのままReact Nativeで使われるため、画面コンポーネントに縛られた構造になる。別に画面を再レンダリングしなくてもよいのに、コンポーネントが生成されようとすると、強制的に再レンダリングをする(※2017年5月15日追記、8月30日更新: react-native-router-fluxを使うことでこれを防ぐことができるが、それでも少し扱いにくいし、完全に意図するように制御するのは難しい)。この仕組みが世界観的にはWebアプリケーションの作りによく似ている。純粋なAndroidでは、View(Acitivity)以外にServiceと呼ばれる画面に紐付かないプログラムも記述できる。React Nativeでも、バックグラウンドタスクを実行できるようだが、react-native-workersのようなパッケージに頼るしかないようだ。AndroidのServiceはマルチスレッドを意識したプログラミングになるが、React Nativeでバックグラウンド処理をするとしても、そもそもJavaScriptはシングルスレッドであることから、刺さる処理があるとアプリケーションの動作に影響があるため、よく考えて処理を書く必要がありそうだ(※2017年6月6日追記: 出力コードの実装状況まで見ていなく推測であったため削除)。

画面間での情報の受け渡しに非常に弱く、基本的にはReduxなどの外部でstateを管理するシステムを使うことになる

Reactの基本思想はコンポーネントに閉じていて、「親から子へ、単方向のデータの流れです」なんていう設計思想であるが、じゃあネイティブアプリみたいに大量の画面が発生して、他画面に変更通知をするにはどうすればいいの?という状況では、お分かりの通りReact単体ではこの問題は解決することはできず、Reduxなどの他の手段に頼ることになる。ネイティブアプリ向けライブラリなんだからこのへんもしっかりサポートしようよ…と思うのだが、おとなしくReduxのような、コンポーネントに閉じずに外部でもstateを管理するシステムを使えということらしい。一方で、Reduxにみるようなコンポーネントへの更新通知に頼らず、なんなら情報を画面おきにクライアントDBにでも保存していってしまえ!という思想の場合、データストアにKVSのAsycStorageがあるじゃないか、と言われる方もいると思うが、たまにアクセスに失敗することがあったり、ちょっと挙動が怪しいことがある(※事象としてはiOSシミュレータ上で、たまにAsyncStorageへのアクセスでコケることが確認できた。なので、AsyncStorageは必要以上には使わないようにしている。実機では今のところは同様の現象は未確認)(2017年9月11日追記:AsyncStorageのアクセス不備も特にこれ以後開発上遭遇しなかったので、データアクセスの失敗がレアケースという認識のもと削除)。Reduxによる他画面への更新通知以外に、データストアの力で解決するような他の逃げ方でいうと、RealmのReact Native版を使うことを思いついたが、これは未検証である。

プッシュ通知が公式では最新のv0.45でもiOSしかサポートされていない

見出しの通りで、結局Androidも両対応となると、react-native-push-notificationのようなパッケージに頼るしかない。または、Firebaseが前提になるが、react-native-fcmを使うなど(プロジェクトでは、Androidの個別識別情報を取得するのに苦労し、Firebaseを使用した。react-native-fcmの取扱いも難しくはなく、Firebaseとreact-native-fcmを使うことで、かなり楽にReact Nativeでもプッシュ通知を実装できる)。ネイティブアプリの基本的な機能の実装すら、公式がサポートに消極的な様子が伺える。

総評: React Nativeでは、ネイティブアプリ開発に必要不可欠なものすら公式から提供されていない。なので、色々とパッケージを盛りまくって、盛ったパッケージの英文資料を読みまくり、JSXタグを打つのが開発の大半となってしまう。Androidについては気合いでなんとかする

React Native開発では、これが定番のパッケージらしいとかを調べ、Railsにgemを入れるが如く、React Nativeに色々とパッケージを盛っていき、該当パッケージの動作検証しつつ、取り入れていくのが主な開発スタイルだ(2017年8月30日追記: 簡素な仕組みのアプリならこれは起きないし、また無論コンポーネントを自作していけばこれは起きない)。なぜこうなってしまうのか。それは、冒頭で述べた通り「React自体がViewしか扱わない」からなのが根本原因として、そしてReact NativeはReactをネイティブアプリの見た目として動作させるだけで、Facebook社の気まぐれによりViewの描画機能を中心とし、各モバイルOSへのブリッジを担ういくつかのAPIを提供しているだけのプラットフォームだ(※あくまでv1,0以下、v0.42の感想)。クライアントアプリケーションを作る際の環境にするためにパッケージをどんどん追加していかないとReact Nativeが使い物にならないから、そうするしかない。そのパッケージを追加していく過程では、英文資料がやたら出てくるのでもちろんそれは読むし(公式が出してるパッケージなどなく、有志が公開しているパッケージなのですごく雑な英語なので読みづらいし、情報量が少ない)、英文が理解できたら資料の通りにJSXタグを打つのみだ。それでうまくいけば良いが、思いの外、Androidの動作状況が芳しくなく、色々と頭を悩ませる。それは気合いでなんとかする。

最後までつらいことづくしだが、React Native開発で最もつらいことは、開発時間のうち調査にかける時間が多く、もっとこうするとユーザ体験が向上するとか、ロジックの部分とか、本来クライアントアプリを作る上で最も労力を割くべき工程に頭と時間を使えていないことだ。あと、アップデート頻度がだいたい2週間に一回と頻度が多い。React Native開発では、基本的には最新安定版を使うことが推奨されるので、これに常にキャッチアップしていく必要がある。

参考までに、自分以外でチーム内の打ち合わせでポロッとでたコメントを書いておく(2017年6月4日追記)。

  • こだわりが強いほど実現しにくい箇所がある
  • 思ったより、クロスプラットフォームじゃない。UIが見た目から違う部分が散見できる。元ゲーム系の人間からすると結構ショック(※Unity経験者)(2017年6月12日訂正、Androidでの動作について評価の変更があったため削除)

仮にアプリケーションの品質向上に時間を使うとしても、React Nativeを使ってiOS、AndroidともにSwiftやJavaで書かれたよく工夫されたネイティブアプリケーション並みの品質のアプリ向けを作るには、想像以上の労力を要するという感覚がある。なんにせよ現行はv1.0以下なので、色々言いたいことはあるのだが、開発バージョンとしてみればこんなものなんだと思う。

(広告)

Learning React Native: Building Native Mobile Apps With Javascript

新品価格
¥7,264から
(2017/8/20 01:51時点)

Article Tags
mmiyauchi

プログラムを書きながらTranceを聴くのが良いですね。みなさんも聴いたほうがいいですよ、Trance。EDMよりハードトランスでしょ。

Related article

React Nativeでファイルの拡張子に「.jsx」を使用していたら、UnableToResolveErrorが出て困った

※この記事はReac…

React Nativeで「No bundle url present」というエラーが出たときの対処

※この記事はReac…

Meteorのブームが来るか来ないと言われれば、来ないと思ってしまった件

2016年秋頃から、…

6 Discussion to this post

  1. 7番目の宮内 より:

    Ver 5000兆くらいになったら、使えるようになるんですかね?

    • mmiyauchi より:

      >> 7番目の宮内さん

      コメントありがとうございます。
      そうですね、React Nativeがv5,000,000,000,000になる頃には、もはやReact Nativeは既に時代遅れになっており、あたらしく「React Real」が出てきて、
      JSXタグで現実世界の物質もレンダリングできる程度には進化しているんじゃあないでしょうか。

      世の中、たいていのことはJSXタグでラップしてrenderメソッドを実行すれば解決するような素晴らしい世界になると良いですね!

  2. 8番目の宮内 より:

    og:imageが真っ白です。

    • mmiyauchi より:

      >> 8番目の宮内さん

      コメントありがとうございます。
      今日は宮内がやけに多いですね。

      たしかに、そうですね。
      日曜日の昼下がりで気分で書いたポエムなので、記事にサムネ画像を設定していなかったです。 とりあえずReactのロゴをサムネ画像にしておきました。

  3. 9番目の宮内 より:

    Unreal Engine, Unity, Xamarin… の比較資料まーだー?

    • mmiyauchi より:

      >>9番目の宮内さん

      コメントありがとうございます。
      Unreal Engineだけ異彩を放ち過ぎですが、XemarinとかCordovaとは明らかに競合なので比較してみるのは面白ろそうですね!
      React Nativeを使ってみて、ネイティブアプリのハイブリッド開発環境って、よくある毎度騙されてしまう「最新の」ソレみたいな感じに思えました。
      今は心が弱っているので、しばらくハイブリッドな開発環境はさけます。。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

Type your search keyword, and press enter to search
%d人のブロガーが「いいね」をつけました。