メインコンテンツまでスキップ

Expo SDK 46アップグレード

Expo SDK 46の主な変更

React Native 0.69.5

React Nativeのバージョンが0.69.5にアップグレードされました。React Nativeの0.69では、大きく以下の変更が実施されています。

React 18

React Native 0.69では、デフォルトでReact 18が有効化されています。React 18はSuspenseのサポートやAutomatic Batching、Transitionsなど多くの機能がリリースされています。それらの多くは、Concurrent Renderingという基盤に支えられています。

React Nativeでは、New Architectureを有効化することでConcurrent Renderingを使用できます。

また、React 18では型定義の変更があります。特に影響が大きいのは、React.FunctionComponentReact.Componentに実装されていたchildrenが削除されたことでしょう。 コンポーネントに定義されているPropsにchildrenを追加するか、PropsをReact.PropsWithChildrenでラップするなどの対応が必要です。

interface MyButtonProps {
color: string;
children?: React.ReactNode;
}

or

interface MyButtonProps {
color: string;
}
const MyButtonComponent: React.FC<React.PropsWithChildren<MyButtonProps>> = (props) => {}

Expo CLI

Expo CLIが一新されました。Githubのリポジトリも、以前はexpo/expo-cliで管理されていましたが、expo/expoに移行されています。

新しいExpo CLIでは、PNPMのサポートや--fixオプションによる依存ライブラリのバージョン修正機能が追加されています。 また、グローバル領域にExpo CLIをインストールする必要がなくなりました。Expoがpackage.jsondependenciesに含まれている場合は、node_modules配下のExpo CLI(以降Local Expo CLIと呼びます)が使用されます。

  • プロジェクトディレクトリでnpx expo installを実行すると、Local Expo CLIが使用されます
  • プロジェクトディレクトリでexpo installを実行すると、グローバル領域にインストールされたExpo CLIが使用されます
  • package.jsonのscriptsに{"scripts": {"start": "expo start"}}と定義されている場合は、プロジェクトディレクトリでnpm startを実行するとLocal Expo CLIが使用されます

ただし、新しいExpo CLIにはexpo upgradeexpo doctorが実装されていません。そのため、Expo SDKのアップグレード作業などは、旧Expo CLIを使用する必要があります。 新しいExpo CLIがインストールされた環境で旧Expo CLIを使用する場合は、expo-cli upgradeのようにexpo-cliコマンドを使用します。

旧Expo CLIからのマイグレーションに関する詳細は、Expo CLI Migrationを参照してください。

Expoがサポートするライブラリ

ライブラリの追加

以下のライブラリが新たにサポート対象となりました。

  • React Native Skia
    • 2DグラフィックエンジンのSkiaをReact Nativeから使用可能にするライブラリ
  • FlashList
    • FlatListにおけるFPSやメモリ使用に関するパフォーマンスの課題を解決してくれるライブラリ

ライブラリの削除

Expo SDK 45で非推奨となっていたライブラリが削除されました。

マイグレーション方法については、上記ライブラリのリンク先を参照してください。

非推奨となるライブラリ

expo-error-recoveryは非推奨となります。Classic Buildでは必要でしたが、EAS Buildでは不要になったのが理由のようです。 Expo SDK 47では、expo-error-recoveryが削除される予定です。

iOSの最小サポート対象バージョンが12.4に変更

React Native 0.69からは、iOSのサポート対象バージョンが12.4以降になります。

なお、iOS 16はExpo SDK 47でサポート対象になる予定です。iOS 16がサポートされると同時に、iOS 12のサポートは対象外になる見込みです。

Expo SDK 43のサポート終了

Expo SDK 43がサポート対象外になりました。Expo SDK 43を使用している場合は、Expo SDK 44以降にアップグレードする必要があります。

vscode-expoの機能追加

vscode-expoで、全てのExpo configsがサポートされるようになりました。

app.jsonapp.config.jsonと同様に、以下のConfigファイルもAutoCompleteやValidationの機能が提供されます。

  • eas.json(EAS Build and Submit)
  • store.config.json(EAS Metadata)
  • expo-module.config.json(Expo modules)

Classic BuildからEAS Buildへのマイグレーション

Expo SDK 46が、Classic Buildをサポートする最後のバージョンとなります。expo build:androidexpo build:iosを使用している場合は、次回のExpoアップグレードまでにeas buildへ移行する必要があります。

マイグレーションの詳細は、Migrating from "expo build"を参照してください。

Classic UpdateからEAS Updateへのマイグレーション

EAS Updateが正式にリリースされています。Classic Updateより速く、柔軟なアップデートシステムを提供しているようです。

マイグレーションの詳細は、Migrate from Classic Updatesを参照してください。

このアプリで実施したアップグレードの手順

このアプリでは、以下の作業を上から順に実施してExpo SDK 46にアップグレードしました。

アップグレードを実施したPull Requestはこちらです。

依存ライブラリのバージョン不整合を解消

expo-cli upgradeを実行すると、Expo SDKとExpoがサポートしているライブラリのバージョンが更新されます。その際に、いくつかの依存ライブラリでバージョンの不整合が発生しました。ここではそれらを解消した手順を記載します。

@react-native-picker/picker

Expo SDK 46がサポートしている@react-native-picker/pickerの2.4.2は、peerDependenciesにReact 18が含まれていません。 そのため、React 18をサポートする2.4.3にアップグレードしました。(fix: Change React peerDependencies version

react-native-svg

Expo SDK 46がサポートしているreact-native-svgの12.3.0は、React 18で変更された型定義に対応していませんでした。そのため、Expoのissueのコメントに記載されている12.4.4にアップグレードしました。

@react-native-firebase

expo-cli upgradeを実行すると、@react-native-firebase/appが15.4.0にアップグレードされました。それに合わせて、このアプリで使用しているFirebase関連のライブラリも15.4.0にアップグレードしました。

  • @react-native-firebase/crashlytics
  • @react-native-firebase/messaging

@testing-library/react-hooks

npm uninstall @testing-library/react-hooksを実行して、@testing-library/react-hooksをアンインストールしました。

@testing-library/react-hooksはReact 18をサポートしていません。代わりに、@testing-library/react-nativeを使用します。 詳細は、react-hooks-testing-library - A Note about React 18 Supportを参照してください。

react-native-safe-area-context

Expo SDK 46がサポートしているreact-native-safe-area-contextのバージョンは4.3.1です。しかし、このアプリで使用しているReact Native Elements 3.4.2は、peerDependenciesにreact-native-safe-area-contextのバージョン4以降が含まれていません。

そのため、react-native-safe-area-contextを3.3.2にダウングレードしました。

react-test-renderer@types/react-test-renderer

React 18のアップグレードに伴い、react-test-renderer@types/react-test-rendererも18.0.0にアップグレードしました。

expo-template-bare-minimumの更新履歴を確認

expo-template-bare-minimumの更新履歴を確認して、更新されたファイルをこのアプリに反映しました。

その中で、iOSの場合にReact Native Firebaseの設定と競合する箇所があったので、それを解消した手順を記載します。

該当するexpo-template-bare-minimumのPull Requestはこちらです。

このPull Requestでは、AppDelegate.mmConcurrent Renderingの有効化を判定する値を初期設定する機能が追加されています。 一方このアプリでは、iOSでアプリがバックグラウンドや停止状態中にPush通知を受信した際の対応として、以下に記載されている対応を追加しています。

どちらも、初期設定をNSDictionaryとして作成してアプリに渡しています。そのため、それらの初期設定(NSDictionary)をマージする必要がありました。

React Native Firebase Cloud Messagingでは、RNFBMessagingModule.maddCustomPropsToUserPropsNSDictionaryを渡すことができます。渡されたNSDictionaryは、React Native Firebase Cloud Messagingが生成する初期設定とマージされます。今回は、それを利用することで初期設定のマージを実施しました。

NSDictionary *initProps = [self prepareInitialProps];
NSDictionary *appProperties = [RNFBMessagingModule addCustomPropsToUserProps:initProps withLaunchOptions:launchOptions];

React 18の対応

Expo SDK 46の主な変更に記載した通り、React 18ではSuspenseのサポートやAutomatic Batching、Transitionsなど多くの機能がリリースされています。しかし、それらはReact NativeのNew Architectureを有効化しないと使用できません。

Expoをはじめとするエコシステムでは、New Architectureを完全にサポートしているライブラリはまだ少ないです。このアプリも、依存しているライブラリがNew Architectureに対応するまでは有効化できないのが現状です。

そのため、React 18の対応としては、React 18アップグレードガイドに記載されているTypeScript 型定義の変更のみを実施しました。型定義の全ての変更は、React 18の型定義に関するPull Requestに記載されています。

このアプリでは、Removal of implicit childrenの変更のみが該当しました。対応するコンポーネントの数が多かったので、React 18アップグレードガイドでも紹介されているtypes-react-codemodを利用して修正しました。

types-react-codemodは、React.FunctionComponentのPropsを、React.PropsWithChildrenでラップするように自動修正してくれます。ただし、注意点として以下がありました。

  • childrenを必要としていないコンポーネントも、React.PropsWithChildrenでラップしてしまう
  • コンポーネントのPropsが存在しない場合、React.PropsWithChildren<unknown>のようにジェネリクスの型にunknownが指定されてしまう

これらは動作的には問題ありませんが、不要な定義を残すとコードの可読性を損なうため、types-react-codemodの実行後に手動で削除しました。

修正後のコード例は以下になります。

interface ComponentBProps {
color: string;
}
interface ComponentCProps {
color: string;
}
// コンポーネントのPropsもchildrenも受け取らない場合
const ComponentA: React.FC = () => {}
// コンポーネントのPropsのみ受け取る場合
const ComponentB: React.FC<ComponentBProps> = (props) => {}
// コンポーネントのPropsもchildrenも受け取る場合
const ComponentC: React.FC<React.PropsWithChildren<ComponentCProps>> = ({children, ...props}) => {}

自動テストの修正

@testing-library/react-hooksはReact 18をサポートしていません。代わりに、@testing-library/react-nativeactrenderHookを使用するように修正しました。

ESLintのエラー・警告対応

一部のコンポーネントで、@typescript-eslint/no-throw-literalの警告が発生していました。

unknown型をthrowしていたので、このアプリで用意しているErrorクラスを継承したRuntimeErrorでラップしてthrowするように修正しました。

また、React.VoidFunctionComponent(React.VFC)が非推奨になったことから、eslint-plugin-deprecationのチェックでエラーが発生していました。そのため、React.VoidFunctionComponent(React.VFC)React.FunctionComponent(React.FC)に変更しました。

注記

今回は利用しませんでしたが、types-react-codemodには、React 19の対応としてReact.VFCReact.FCに自動修正してくれる機能も存在します。

詳細は、deprecated-void-function-componentを参照してください。

TSCのエラー対応

一部のTSファイルで、以下のエラーが発生していました。

TS7006: Parameter '_' implicitly has an 'any' type.

使用していない変数の型を省略していたため、型を明示的に指定しました。

React Native Firebase 15の対応

iOSの場合、React Native Firebaseのバージョン15(Firebase iOS SDKのバージョン9)以降では、Frameworkを使用します。Frameworkを使用した場合、React Native Firebaseのドキュメントによると以下の制限があります。

  • React NativeのNew Architectureは有効化できない
  • Flipperは使用できない
  • JavaScriptのエンジンとしてHermesを使用する場合は、Static Frameworkを使用する必要がある

このアプリでは、use_frameworksを使用してFrameworkを有効化して、Flipperを無効化する対応をしました(New Architectureは以前から無効化しています)。

Podfile
+ $RNFirebaseAsStaticFramework = true
/* ~省略~ */
- use_flipper!(configurations: ['Debug', 'DebugAdvanced'])
+ # use_flipper!(configurations: ['Debug', 'DebugAdvanced'])
/* ~省略~ */
Podfile.properties.json
  {
- "expo.jsEngine": "jsc"
+ "expo.jsEngine": "jsc",
+ "ios.useFrameworks": "static"
}

既存のパッチファイルの更新

このアプリでは、patch-packageを使用して、以下のライブラリにパッチファイルを適用してます。パッチ内容の詳細は、こちらを参照してください。

  • @react-native-community+cli-platform-ios
  • expo-splash-screen
  • react-native-reanimated
  • react-native-elements

React Native Elements以外は、Expo SDKのアップグレードに伴いバージョンが上がりました。しかし、適用していたパッチファイルはまだ必要な対応だったため、パッチファイルは削除せずに各ライブラリのバージョンに合わせてファイル名をリネームしました。

React Native Elementsのバージョンは変更していないのですが、React 18のアップグレードに伴い、ThemeProviderのPropsからchildrenが削除されていました。そのため、ThemeProviderのPropsにchildren?: React.ReactNodeを定義するパッチを追加しています。

開発・CIに使用しているツールのバージョンを更新

Expo SDK 46のアップグレードとは関係ありませんが、このタイミングで開発に使用しているツールのバージョンを更新しました。

Toolfromto
Node.js16.15.116.18.0
Javazulu-11.56.19zulu-11.58.23

CIに使用しているツールのバージョンも更新しました。

Toolfromto
Node.js16.15.116.18.0
Xcode13.3.114.0.1

参考記事