React Native ベストプラクティス
概要
React Nativeアプリケーションのパフォーマンス最適化ガイドです。JavaScript/React、ネイティブ(iOS/Android)、バンドル化の最適化をカバーします。Callstack社の「Ultimate Guide to React Native Optimization」に基づいています。
適用タイミング
以下の場合にこれらのガイドラインを参照してください:
- 遅い/ジャンキーなUIやアニメーションのデバッグ
- メモリリークの調査(JS側またはネイティブ側)
- アプリ起動時間(TTI)の最適化
- バンドルサイズまたはアプリサイズの削減
- ネイティブモジュール(Turbo Modules)の作成
- React Nativeのパフォーマンスプロファイリング
- パフォーマンスの観点からのReact Nativeコードレビュー
セキュリティ注意事項
- ここで参照するシェルコマンドは、ローカルの開発者操作として扱ってください。実行前に内容を確認し、バージョン固定のツールを優先し、リモートスクリプトをシェルに直接パイプしないでください。
- サードパーティのライブラリやプラグインは、通常のサプライチェーン管理が必要な依存関係として扱ってください。バージョンを固定し、出所を確認し、標準的なレビュープロセスを通じて更新してください。
- リモートチャンクの読み込みは、自社製のアーティファクト配信のみとして扱ってください。アプリにバンドルされたチャンクまたは署名されたCIリリースマニフェストを優先し、ホストされるチャンクは、自社管理の信頼できるHTTPSオリジンから提供され、現在のアプリリリースに固定されている必要があります。
優先順位付けされたガイドライン
| 優先度 | カテゴリ | 影響 | プレフィックス |
|---|---|---|---|
| 1 | FPSと再レンダリング | CRITICAL | js-* |
| 2 | バンドルサイズ | CRITICAL | bundle-* |
| 3 | TTI最適化 | HIGH | native-*, bundle-* |
| 4 | ネイティブパフォーマンス | HIGH | native-* |
| 5 | メモリ管理 | MEDIUM-HIGH | js-*, native-* |
| 6 | アニメーション | MEDIUM | js-* |
影響ラベルはトリアージのヒントです:CRITICALを最初に、次にHIGH、そして証拠が示す場合にMEDIUMを確認してください。
クイックリファレンス
最適化ワークフロー
パフォーマンス問題が発生した場合は、次のサイクルに従ってください:測定 → 最適化 → 再測定 → 検証
- 測定:変更前のベースラインメトリクスを取得します。ランタイムの問題については、コミットタイムライン、再レンダリング回数、低速コンポーネント、最もコストのかかるコミットの内訳、起動時間/TTI(利用可能な場合)を優先してください。コンポーネントツリーの深さや数は代替情報ではなく、あくまでオプションのコンテキストです。測定されたレンダリングやFPSの問題がない限り、メモ化、アトミックステート、コンパイラの変更を推奨しないでください。
- 最適化:該当するリファレンスからターゲットを絞った修正を適用します。
- 再測定:同じ測定を実行し、更新されたメトリクスを取得します。
- 検証:改善を確認します(例:FPS 45→60、TTI 3.2秒→1.8秒、バンドル 2.1MB→1.6MB)。
メトリクスが改善しなかった場合は、変更を元に戻し、次の推奨修正を試してください。
レビュー時のガードレール
- API固有の修正を提案する前に、ライブラリのバージョンを確認してください。例:FlashList v2では
estimatedItemSizeが非推奨であるため、そこで欠落しているフラグを立てないでください。 - 動作が明らかに間違っている場合、またはプロファイリングがその値に関連する無駄な処理を示していない限り、
useMemoやuseCallbackの依存関係の変更を提案しないでください。 - 推測でクロージャの古い値(stale closure)を報告しないでください。それを指摘する前に、古い値を読み取るパス、再現手順、またはプロファイラの証拠を示してください。
- フローをプロファイリングする際は、対象のインタラクション自体を測定してください。コンポーネントツリーの深さやコンポーネント数を主要なパフォーマンスの証拠として扱わないでください。
最重要:FPSと再レンダリング
まずプロファイリングを実行:
agent-device react-devtools status
agent-device react-devtools wait --connected
agent-device react-devtools profile start
agent-device react-devtools profile stop
agent-device react-devtools profile slow --limit 5
agent-device react-devtools profile rerenders --limit 5
agent-device react-devtools profile timeline --limit 20
profile startとprofile stopの間で、通常のagent-deviceコマンドで対象のインタラクションを操作してください。
agent-deviceが利用できない場合の手動フォールバック:Metro(j)またはデバッグメニューからReact Native DevToolsを開き、Profilerタブを使用して同じインタラクションを記録してください。
リリースビルドのReactコンポーネントプロファイリングの場合は、最初に@callstack/inspectorを接続してReact DevToolsがリリースアプリにアタッチできるようにし、その後上記のagent-device react-devtoolsフローを実行してください。
一般的な修正:
- 長いリストに対しては、ScrollViewをFlatList/FlashList/Legend Listに置き換える
- プロファイリングが連鎖的な再レンダリングを示した後は、React Compilerを使用して自動メモ化を行う
- プロファイリングが広範なストア/コンテキスト更新を示した後は、アトミックステート(Jotai/Zustand)を使用して再レンダリングを削減する
- 高コストな計算には
useDeferredValueを使用する
最重要:バンドルサイズ
バンドルの分析:
npx react-native bundle \
--entry-file index.js \
--bundle-output output.js \
--platform ios \
--sourcemap-output output.js.map \
--dev false --minify true
npx source-map-explorer output.js --no-border-checks
最適化後の改善の確認:
# 変更前のベースラインサイズを記録
ls -lh output.js # 例:Before: 2.1 MB
# 修正適用後、再バンドルして比較
npx react-native bundle --entry-file index.js --bundle-output output.js \
--platform ios --dev false --minify true
ls -lh output.js # 例:After: 1.6 MB (24%削減)
一般的な修正:
- バレルインポートを避ける(ソースから直接インポートする)
- HermesのAPIとメソッドのカバレッジを確認した後にのみ、不要なIntlポリフィルを削除する
- ツリーシェイキングを評価する(Expo SDK 52+の実験的な未使用インポート/エクスポート削除、または既に設定されている場合のみRe.Pack)
- Androidのネイティブコード縮小のためにR8を有効にする
高優先度:TTI最適化
TTIの測定:
react-native-performanceをマーカーに使用する- コールドスタートのみを測定する(ウォーム/ホット/プレウォームは除外する)
一般的な修正:
- React Native 0.78以前では、AndroidのJSバンドル圧縮を無効にしてHermesのmmapを有効にする
- ネイティブナビゲーション(react-native-screens)を使用する
- よく使われる高コストな画面に遷移する前に、それらをプリロードする
高優先度:ネイティブパフォーマンス
ネイティブのプロファイリング:
- iOS:Xcode Instruments → Time Profiler
- Android:Android Studio → CPU Profiler
一般的な修正:
- 高負荷なネイティブ処理にはバックグラウンドスレッドを使用する
- 同期Turbo Moduleメソッドよりも非同期を優先する
- クロスプラットフォームのパフォーマンス重視のコードにはC++を使用する
リファレンス
完全なドキュメント(コード例付き)はreferences/にあります:
JavaScript/React (js-*)
| ファイル | 影響 | 説明 |
|---|---|---|
| js-lists-flatlist-flashlist.md | CRITICAL | ScrollViewを仮想化リストに置き換え |
| js-profile-react.md | MEDIUM | agent-device react-devtoolsプロファイリング |
| js-measure-fps.md | HIGH | FPS監視と測定 |
| js-memory-leaks.md | MEDIUM | JSメモリリークの追跡 |
| js-atomic-state.md | HIGH | Jotai/Zustandパターン |
| js-concurrent-react.md | HIGH | useDeferredValue, useTransition |
| js-react-compiler.md | HIGH | 自動メモ化 |
| js-animations-reanimated.md | MEDIUM | Reanimatedのワークレット |
| js-bottomsheet.md | HIGH | ボトムシートの最適化 |
| js-uncontrolled-components.md | HIGH | TextInputの最適化 |
ネイティブ (native-*)
| ファイル | 影響 | 説明 |
|---|---|---|
| native-turbo-modules.md | HIGH | 高速なネイティブモジュールの構築 |
| native-sdks-over-polyfills.md | HIGH | ネイティブライブラリ vs JSライブラリ |
| native-measure-tti.md | HIGH | TTI測定のセットアップ |
| native-threading-model.md | HIGH | Turbo Moduleのスレッド |
| native-profiling.md | MEDIUM | Xcode/Android Studioでのプロファイリング |
| native-platform-setup.md | MEDIUM | iOS/Androidツールガイド |
| native-view-flattening.md | MEDIUM | ビュー階層のデバッグ |
| native-memory-patterns.md | MEDIUM | C++/Swift/Kotlinのメモリ |
| native-memory-leaks.md | MEDIUM | ネイティブメモリリークの追跡 |
| native-android-16kb-alignment.md | CRITICAL | Google Play向けサードパーティライブラリアライメント |
バンドル (bundle-*)
| ファイル | 影響 | 説明 |
|---|---|---|
| bundle-barrel-exports.md | CRITICAL | バレルインポートを避ける |
| bundle-analyze-js.md | CRITICAL | JSバンドルの可視化 |
| bundle-tree-shaking.md | HIGH | デッドコードの除去 |
| bundle-analyze-app.md | HIGH | アプリサイズの分析 |
| bundle-r8-android.md | HIGH | Androidコードの縮小 |
| bundle-hermes-mmap.md | HIGH | バンドル圧縮の無効化 |
| bundle-native-assets.md | HIGH | アセットカタログのセットアップ |
| bundle-library-size.md | MEDIUM | 依存関係の評価 |
| bundle-code-splitting.md | MEDIUM | リモートチャンク読み込みのセキュリティ対策 |
問題 → スキルマッピング
| 問題 | 最初に参照するもの |
|---|---|
| アプリが遅い/ジャンキー | js-measure-fps.md → js-profile-react.md |
| 再レンダリングが多すぎる | js-profile-react.md → js-react-compiler.md |
| 起動が遅い(TTI) | native-measure-tti.md → bundle-analyze-js.md |
| アプリサイズが大きい | bundle-analyze-app.md → bundle-r8-android.md |
| メモリが増加している | js-memory-leaks.md または native-memory-leaks.md |
| アニメーションがフレームをドロップする | js-animations-reanimated.md |
| ボトムシートがジャンキー/再レンダリング | js-bottomsheet.md → js-animations-reanimated.md |
| リストスクロールがジャンキー | js-lists-flatlist-flashlist.md |
| TextInputの遅延 | js-uncontrolled-components.md |
| ネイティブモジュールが遅い | native-turbo-modules.md → native-threading-model.md |
| ネイティブライブラリアライメントの問題 | native-android-16kb-alignment.md |
帰属
Callstack社の「The Ultimate Guide to React Native Optimization」に基づいています。