React Nativeの最近のバージョンアップはマイナーバージョンが以前は毎月だったものが約3ヶ月毎にリリース期間が長くなっており、その間、1ヶ月に数回のペースでパッチバージョンがリリースされ、今まで致命的なバグが放置されてきたものが漸く解消されてきました。
現在従事しているプロジェクトではv0.55.4までアップグレードしたところで、バグが解消されないのでアップグレード出来てませんでしたが、v0.56でReact Nativeの内部が再構築され、v0.57で多くの問題が解消していると判断したのでそろそろ良いタイミングだということでアップグレードすることにしました。
目次
スポンサーリンク
v0.55からv0.57で加わった大きな変化
Babel7にアップデート
正確にはReact Native v0.56で加わった変更で、Babelが7にメジャーアップしました。React Nativeとして主な影響はBabelモジュールを@babel
スコープに変更したこと、JSプリセットがes2015
のような年号プリセットが廃止されています。
安定性の向上
v0.55.4では(特にAndroidでの)ネットワーク関連、日本語入力関連で致命的なバグがあり苦しい回避策を講じる必要がありましたがv0.57.8では解消しています。詳細については後述します。
アップグレード手順
公式ドキュメントでアップグレード手順を把握
Upgrading to new React Native versionsに基本的な手順が、react-native-releases/CHANGELOG.mdにバージョン別の変更履歴とアップグレードの注意が書いてあるので読んでおきます。
react-native upgradeにてアップグレード
既存ソース部分のアップグレードはNativeモジュールを利用しているReact Nativeプロジェクトの場合はreact-native-git-upgrade
とreact-native upgrade
の2通りの方法があります。
公式で推奨するのはreact-native-git-upgrade
の方で、package.jsonのバージョン更新まで自動で行ってくれるので便利なのですが、今回適用しようとしたプロジェクトではpackege.jsonやNative側のソースでかなり手を加えていたからか、実行時に競合が発生してエラーで終了してしまい利用出来ませんでしたのでreact-native upgrade
の方法を利用しました。手順は以下の様になります:
- package.jsonの
react-native
を0.57.8
に、react
及びreact-test-renderer
を16.6.3
に変更 $ yarn
$ react-native upgrade
- 競合が大量に出てファイル毎に取り扱い方法を対話形式に出るのでとりあえず.gitignore以外は全部上書きしてなにが書き換わるか確認する
- ic_launcher.png等のアイコン系ファイルは追加すると公式アイコンを消してしまうので全て削除する
- 他の差分はappで独自に追加したモジュール類が上書きで削除されているので元に戻す
- 根気の要る作業なので数時間かける覚悟を持ってやる
$ react-native run-ios(android)
これで一応、アプリが立ち上がるようになりました。この時点では複数のwarningと、機能によっては動作しない部分がありますので詳細は1つづつ紹介していきます。
Nativeソースを自分で書き換えたソース部分に関しては上手くマージすることが出来ず、iOSのproj内コードまでも競合して手動で変更せざるを得なかったところが非常に苦労しました。この辺はアップグレードの度に毎回作業を強いられるととても辛いのでReact Native側に頑張って貰いたいところです。
実行してみて躓いたところとその対応方法
HtmlViewモジュールで Failed prop type: Invalid prop 警告
react-native-html-view
を利用している画面を参照すると以下の様な不正prop type警告が発生しました:
warning: Failed prop type: Invalid prop
`RootComponent` of type `object` supplied to
`HtmlView`, expected `function`.
in HtmlView (at PresentIndexScreen.js:107)
Fix error on react native version 0.56 caused by defaultProps for RootComponent by alphasp · Pull Request #228 · jsdf/react-native-htmlviewを参照すると、react-native-html-view v0.13.0
で解消しているのでアップグレードします。
Animated: 'useNativeDriver' is not supported 警告
app起動時にAnimated: 'useNativeDriver' is not supported
警告が発生しました:
Animated: `useNativeDriver` is not supported
because the native animated module is missing.
Falling back to JS-based animation. To resolve this,
add `RCTAnimation` module to this app, or remove
`useNativeDriver`. More info: https://github.com/
facebook/react-native/issues/11094#issuecomment-263240420
Button component generate warning · Issue #11094 · facebook/react-nativeを参考にするとlibRCTAnimation.a
が正しく読み込めて無かったのでXCodeでモジュールを再追加しました。
画面遷移するとreact-native-safe-area-viewで以下の様な循環参照エラーが出ました:
Require cycle: node_modules/react-native-safe-area-
view/index.js -> node_modules/react-native-safe-
area-view/withSafeArea.js -> node_modules/react-
native-safe-area-view/index.js
react-navigation v2.12.1ではreact-native-safe-area-view v0.9.0に依存しており、このバージョンだと内部で循環参照が発生していました:
Require cycle in withSafeArea.js line 4 · Issue #40 · react-native-community/react-native-safe-area-view
react-navigation v2.12.1→v2.18.3にアップデートすることでreact-native-safe-area-view v0.9.0→v0.11.0にアップデートされ、循環参照警告が解消されました。
iOSで日本語入力が出来ない
react-native v0.55.4 iOSではTextInputで日本語入力が出来ない致命的なバグが有り、それを回避する為の対策コードを実装していました。
コメント内にいくつか回避策がありますが、これを適用した状態で v0.57.8にアップグレードすると逆に日本語が打てなくなる場合があるので回避コードを削除します。
TextInput.clear()サポートに伴う暫定対応解除
TextInput
の入力をリセットするTextInput.clear()
メソッドがv0.55.4では効かなかったのでユーザーが入力した文字列をリセットする際は一旦仮想DOMから外してre-renderして解除していました:
[0.54] TextInput.setNativeProps({text: ''}) no longer works · Issue #18272 · facebook/react-native
こちらはv0.57でサポートされたので入力テキストを消す部分の暫定対応を解除しました。以下は独自で定義した内部で使っているTextInputをonRef
でオブジェクトを返すTextFieldコンポーネントで、入力確定するとonSubmit
が呼ばれるように実装していた場合に入力テキストを消してフォーカスを外す様にした例です:
<TextField
onSubmit={text => {
this.textInput.clear();
this.textInput.blur();
}}
onRef={ref => (this.textInput = ref)}
/>
Gifアニメーションしない
ImageコンポーネントでGifアニメーションを利用するために標準で組み込まれているfresco:animated-gifを利用していましたがv0.57.8のAndroidで表示はされるがアニメーションしなくなりました。
Issue with compile 'com.facebook.fresco:animated-gif:1.8.1' · Issue #18866 · facebook/react-nativeのコメントによると、RN Versionによってcom.facebook.fresco:animated-gif
のバージョンが変わるとのこと。
RN Version | Fresco Version |
<= 0.55 | <1.3.0 |
0.56 | 1.9.0 |
0.57 | 1.10.0 |
android/app/build.gradle
のanimated-gif
バージョンを1.10.0
にアップデートすることでアニメーションするようになりました:
implementation "com.facebook.fresco:animated-gif:1.10.0"
以前のバージョンではcom.facebook.fresco:animated-base-support:1.3.0
がありましたが新しいバージョンが無くなっています。これはAndroid 4.0より以前のバージョンをサポートしたい場合に組み込むようでしたが、既にターゲットバージョンから外していたのでこのタイミングで削除しました。
babel-preset-react-nativeモジュールの廃止
react-native-releases/CHANGELOG.md at master · react-native-community/react-native-releasesより、v0.56 → v0.57 でJS presetがbabel-preset-react-native
からmetro-react-native-babel-preset
に変更しました:
{
"presets": ["module:metro-react-native-babel-preset"]
}
それに伴い、metro-react-native-babel-preset
はreact-nativeに標準で依存しているのでなにも新しくインストールする必要は無いのですが、babel-preset-react-native
は利用することが無くなったので古いモジュールをpackage.json
より削除しました。
NetInfo.isConnected.fetch()を複数同時リクエストするとレスポンスが返ってこない
RN0.55.4だと、API呼び出しなどでfetch()
を呼び出した際、オフラインなどの理由で失敗するとAndroidでクラッシュするという致命的なバグがありました。
そのため、オフライン時には呼び出しを発生させないように事前にNetInfo.isConnected.fetch()
で現在のネット接続状況を確認してからfetch()
を呼び出していました。
Android crash when network changes from Good to Bad · Issue #10423 · facebook/react-native
それが今度はRN0.57.8ではiOSでNetInfo.isConnected.fetch()
を起動時は複数のAPIを呼び出し必要があって複数同時リクエストしたら1つ以外のresolveが返ってこないという別の致命的なバグが発生してしまいました...
2019/01/25現在でもこのIssueはOpenでまだ直って無い様ですが、fetch()
の方はv0.57.8にアップグレードすることでオフライン時でもクラッシュせずにエラーで返ってくるようになったのでNetInfo.isConnected.fetch()
の事前チェックを廃止することでこの問題を回避してAPI呼び出しを行う事が出来る様になりました。
まとめ
まだ不安定な部分もあるんですが、最近のメジャーバージョンアップは少しづつ致命的なバグが取り除かれてきており、枯れたフレームワークへのスタートラインに立ちつつあります。
そうこうしているしている間に先日React Native v0.58.1がリリースされましたが、まだ情報も殆ど無く、えいや、とアップグレードする勇気は無いのでバグFIXがある程度入ったパッチバージョンがインクリメントされたあたりでこちらも試してレポートしたいと思います。