6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

古いReact NativeアプリをNew Architectureへ上げ直す

6
Last updated at Posted at 2026-09-26

はじめに

数年前に作ったReact Nativeアプリを久しぶりに開くと、JavaScriptのコードそのものより、周辺環境の変化に戸惑うことがあります。

React Nativeでは0.76からNew Architectureがデフォルトになり、0.82からはNew Architectureのみで動作する構成になりました。さらに0.87ではStrict TypeScript APIがデフォルトになり、Node.js 22、Android Gradle Plugin 9、Kotlin 2.0以上が要件になっています。

私はReact Native、Flutter、Swiftを案件に応じて使い分けています。また、jQueryからReactへの移行や古いコードの作り直しにも関わってきました。そういう作業で意識しているのは、「とりあえず最新版へ上げる」より先に、何が古くなっているのかを分解することです。

この記事では、古いReact Nativeアプリを現在の構成へ持っていくと仮定して、私ならどの順番で調べるかをチェックリストにしました。特定案件の移行記録ではなく、既存アプリを調査するときの再利用可能な手順としてまとめています。

結論

React Nativeの更新は、react-nativeのバージョンだけを上げる作業ではありません。

私は「現状把握 → 依存ライブラリ → iOS/Android → New Architecture → アプリ動作」の順に分けます。一気に変更せず、壊れた場所を特定できる単位で進めるのが基本方針です。

環境

この記事では、2026年9月時点で安定版として公開されているReact Native 0.87系を最終的な移行先の例にします。公式のリリース一覧では0.87系と0.86系がActiveで、0.88は2026年10月リリース予定です。

前提は次のとおりです。

  • React Native 0.87系
  • TypeScript
  • Node.js 22系
  • JDK 17
  • Android Studio
  • Xcode 最新版
  • CocoaPodsを使う既存iOSプロジェクト

React Native公式ドキュメントでは、現在Node.js 22.11.0以上、JDK 17を案内しており、iOSについては最新Xcodeの利用を案内しています。React Native 0.87ではAndroid Gradle Plugin 9とKotlin 2.0以上も要件になりました。

なお、この記事では「数年前のアプリ」が何系のReact Nativeだったかを固定しません。

0.6x系から上げる場合と0.7x系から上げる場合では必要な変更量が違うからです。古いバージョンを適当に決めてしまうより、「現在地を確認してから差分を読む」という手順そのものを持ち帰れる形にします。

手順

1. 最初にコードを書かず、現在地を記録する

最初にやることはアップデートではありません。

既存状態の記録です。

まずpackage.jsonを確認します。

node -v
npm -v
npm ls react react-native

Yarnを使っているプロジェクトなら、利用しているYarnのバージョンも確認します。

yarn --version

次に、ネイティブ側を確認します。

cd android
./gradlew --version
cd ..

iOSではRuby、CocoaPods、Xcodeなど、現在ビルドに使っている環境を確認します。

ruby -v
pod --version
xcodebuild -version

ここで重要なのは、「最新かどうか」を判定することではありません。

現在のアプリが何に依存しているかを記録することです。

私はこの段階を、古い家のリフォーム前の現地調査に近いと考えています。壁を壊してから「ここに配管があったのか」と気付くより、先に図面を作ったほうが安全です。

Gitについても、変更前の状態へ確実に戻れるようにしておきます。

git status
git log -1 --oneline

さらに、更新前の状態で可能ならiOSとAndroidを一度ビルドします。

古い環境ではすでにビルドできなくなっている可能性もあります。その場合は、それ自体を記録します。

「アップデートしたら壊れた」のか、「アップデート前から壊れていた」のかは、後になるほど分からなくなります。

2. package.jsonを「棚卸し表」として読む

次に依存ライブラリを分類します。

私なら少なくとも次の4種類に分けます。

A. JavaScriptだけで完結するもの
B. iOS/Androidのネイティブコードを含むもの
C. React Native本体との結びつきが強いもの
D. 自作Native Module / Native Component

特にB、C、Dを重点的に確認します。

New Architectureでは、ネイティブモジュールとネイティブコンポーネントの仕組みとしてTurbo Native ModulesやFabric Native Componentsが使われます。一方、古い方式向けのライブラリでもInterop Layerによって動作するものがあります。公式ドキュメントでも、古いAPIについて代替ライブラリへの移行、New Architecture対応版への更新、自前での移植が選択肢として挙げられています。

つまり、

古いNative Moduleがある
        ↓
即座に全面書き直し

とは限りません。

ただし、

今はInterop Layerで動く
        ↓
今後も何も確認しなくてよい

でもありません。

この区別は大切です。

3. 自作ネイティブコードを検索する

古いプロジェクトでは、package.jsonだけを見ても全体像が分からないことがあります。

アプリ側へ直接Swift、Objective-C、Java、Kotlinのコードが追加されていることがあるからです。

私はまずファイルを探します。

find ios -type f \( -name "*.m" -o -name "*.mm" -o -name "*.h" -o -name "*.swift" \)
find android -type f \( -name "*.java" -o -name "*.kt" \)

さらに、React Nativeとの接続部分らしいコードを検索します。

grep -R "RCTBridgeModule\|RCTViewManager" ios
grep -R "ReactContextBaseJavaModule\|ViewManager" android

該当したからといって、すべて問題になるわけではありません。

目的は「自分たちで面倒を見る必要があるコード」を発見することです。

ライブラリなら更新版を探せますが、自作コードには自動的に新しい版が出てきません。

4. Upgrade Helperでテンプレート差分を確認する

React Native公式は、既存プロジェクトのアップグレードでUpgrade Helperを利用するよう案内しています。React NativeプロジェクトはJavaScriptだけではなく、AndroidプロジェクトとiOSプロジェクトも含むため、単純なパッケージ更新だけでは済まないからです。

ここで見るのは、アプリのソースコードだけではありません。

たとえば次のようなファイルです。

android/build.gradle
android/app/build.gradle
android/gradle.properties
android/settings.gradle
android/gradle/wrapper/gradle-wrapper.properties

ios/Podfile
ios/AppDelegate.*
ios/*.xcodeproj

package.json
babel.config.*
metro.config.*

古いプロジェクトほど、現在のReact Nativeテンプレートとの差が積み重なっています。

このとき私は、「自分のファイルを最新テンプレートへ無理やり合わせる」のではなく、差分の理由を一つずつ確認します。

テンプレートとの差には、

React Native更新による変更
プロジェクト独自設定
ライブラリ導入時の変更
過去の暫定対応
現在は不要になった設定

が混ざっているからです。

全部を一括置換すると、独自設定まで消してしまう可能性があります。

5. 大きなバージョン差を一回の変更として扱わない

React Native 0.76ではNew Architectureがデフォルトになりました。0.80ではLegacy Architectureが凍結され、0.82ではNew Architectureのみになっています。0.84ではLegacy Architecture関連コードの削除がさらに進み、0.87ではStrict TypeScript APIがデフォルトになりました。

数年前のアプリから0.87へ移る場合、これらを「React Nativeを更新した」という一つの変更として扱うと、問題の原因を追いにくくなります。

考え方としては、次のように分けます。

実際にどのバージョンを経由するかは、元のバージョンや依存ライブラリによって変わります。

React Native 0.82の公式リリースでは、Legacy Architectureから移行していない場合、まず0.81へ移行してNew Architectureを有効化し、そこで動作確認してから0.82へ進む方法が案内されていました。これは「大きな境界では、一度動く状態を作る」という考え方の参考になります。

6. New Architectureを単独の確認項目にする

古いReact Nativeアプリを現在へ上げるなら、New Architectureは「最後に気が向いたら確認する項目」にはできません。

0.82以降ではNew Architectureが唯一の実行方式だからです。newArchEnabled=falseなどでLegacy Architectureへ戻す設定も0.82では無視されます。

確認対象は主に3つです。

1. サードパーティライブラリ
2. 自作Native Module
3. 自作Native Component

サードパーティライブラリについては、最新版のREADME、リリースノート、issue、React Native DirectoryなどでNew Architecture対応状況を確認します。

自作モジュールについては、古いBridge APIを使っているかを確認します。

自作コンポーネントについてはFabricへの移行が必要かを確認します。

公式にはInterop Layerが残されているため、古いライブラリがすべて即座に動かなくなるわけではありません。しかしReact Native 0.84以降ではLegacy Architectureコードの削除も段階的に進んでいます。長期保守するアプリなら、「今動くか」と「今後も依存し続けたいか」は分けて判断したほうがよいです。

7. TypeScriptのエラーを「邪魔なもの」として消さない

0.87へ上げる場合、もう一つ大きな確認ポイントがあります。

Strict TypeScript APIです。

0.87ではReact Nativeの公開JavaScript APIとしてStrict TypeScript APIがデフォルトになりました。内部パスへのdeep importは型エラーになり、一部の型名や形も変更されています。

古いコードでは、たとえば次のような内部パスへの依存が残っている可能性があります。

import Something from 'react-native/Libraries/...';

こうしたコードが見つかったとき、最初にanyを足して型エラーを消すのは避けます。

型エラーが「古いAPIへの依存」を教えてくれている可能性があるからです。

私は移行時のエラーを、

A. APIが本当に変わった
B. 型定義が厳密になった
C. ライブラリ側が追随していない
D. アプリが内部APIへ依存している

に分けて考えます。

0.87には以前の型定義へ一時的に戻すオプトアウトも用意されていますが、公式には一時的な移行手段として位置付けられています。

型エラーを減らすことではなく、なぜエラーになったのかを理解することを優先します。

8. iOSとAndroidを別々の移行として扱う

React Nativeはクロスプラットフォームですが、アップグレード時までiOSとAndroidが同じ問題を起こすわけではありません。

iOSでは、たとえば次を確認します。

Podfile
AppDelegate
Deployment Target
Build Settings
Build Phases
ネイティブライブラリ
権限設定

Androidでは次のような場所を確認します。

Gradle
Android Gradle Plugin
Kotlin
MainApplication
MainActivity
AndroidManifest.xml
compileSdk / targetSdk
ネイティブライブラリ

特に0.87ではAndroid Gradle Plugin 9とKotlin 2.0以上が要件になっているため、React Nativeだけ更新して古いAndroidビルド環境を残すという考え方は取りにくくなっています。

一方、iOSでは0.87でSwift Package Manager対応が実験的に追加されていますが、CocoaPodsは引き続きデフォルトかつサポートされる方法です。既存アプリのアップグレードとパッケージマネージャー変更を同時に行う必然性がなければ、私は別の変更として考えます。

変更点を増やすほど、問題発生時の組み合わせも増えるからです。

9. ビルド成功をゴールにしない

アップグレードで最初に目指すのはビルド成功ですが、それだけでは不十分です。

アプリが起動したあと、ネイティブ機能を中心に確認します。

たとえば一般的なアプリなら、

[ ] 起動できる
[ ] 画面遷移できる
[ ] API通信できる
[ ] ログイン状態を保持できる
[ ] 入力フォームが動く
[ ] キーボード表示でレイアウトが崩れない
[ ] モーダルが動く
[ ] 画像を扱える
[ ] ファイルを扱える
[ ] Deep Linkが動く
[ ] Push通知が動く
[ ] バックグラウンド復帰後も操作できる
[ ] iOS実機で確認した
[ ] Android実機で確認した

もちろん、全部のアプリに全部の項目があるわけではありません。

重要なのは、そのアプリがOSやネイティブAPIと接触する場所を一覧にすることです。

JavaScriptだけの画面表示が正常でも、カメラ、通知、ストレージ、認証、Deep Linkなどで問題が出ることがあります。

10. 最終チェックリスト

ここまでを、実際にコピーして使える形へまとめます。

React Nativeアップグレード確認表

■ 変更前
[ ] 現在のreact-nativeバージョンを記録した
[ ] Reactバージョンを記録した
[ ] Node.jsバージョンを記録した
[ ] JDK / Gradle環境を記録した
[ ] Xcode / CocoaPods環境を記録した
[ ] package.jsonを保存した
[ ] lockfileを保存した
[ ] 更新前のiOSビルド可否を確認した
[ ] 更新前のAndroidビルド可否を確認した
[ ] Gitで戻れる地点を作った

■ 依存ライブラリ
[ ] ネイティブコードを含む依存を抽出した
[ ] 長期間更新されていない依存を確認した
[ ] New Architecture対応状況を確認した
[ ] 代替候補が必要なライブラリを分けた
[ ] 自作Native Moduleを確認した
[ ] 自作Native Componentを確認した

■ React Native本体
[ ] Upgrade Helperで差分を確認した
[ ] package.jsonだけでなくテンプレート差分も確認した
[ ] New Architecture移行を独立した作業として扱った
[ ] Strict TypeScript APIによるエラーを確認した
[ ] deep importへの依存を確認した

■ iOS
[ ] Podfile差分を確認した
[ ] AppDelegate差分を確認した
[ ] Build Settingsを確認した
[ ] Build Phasesを確認した
[ ] 権限設定を確認した
[ ] ネイティブライブラリを確認した
[ ] Simulatorで起動した
[ ] 実機で主要機能を確認した

■ Android
[ ] Gradle差分を確認した
[ ] Android Gradle Pluginを確認した
[ ] Kotlinバージョンを確認した
[ ] MainApplication差分を確認した
[ ] MainActivity差分を確認した
[ ] AndroidManifest.xmlを確認した
[ ] Emulatorで起動した
[ ] 実機で主要機能を確認した

■ アプリ
[ ] 起動
[ ] 画面遷移
[ ] API通信
[ ] 認証
[ ] ストレージ
[ ] 入力フォーム
[ ] キーボード
[ ] モーダル
[ ] Deep Link
[ ] Push通知
[ ] バックグラウンド復帰
[ ] アプリ固有のネイティブ機能

■ 最後
[ ] 不要になった暫定設定を削除した
[ ] warningを確認した
[ ] CIでビルドした
[ ] Release構成で確認した
[ ] 変更理由を残した

このリストで大事なのは、項目数ではありません。

「依存関係」「React Native本体」「iOS」「Android」「アプリ機能」を混ぜないことです。

ハマりどころ

package.jsonを更新しただけで移行した気になる

React Native公式のアップグレードガイドでも、一般的なReact NativeアプリはAndroid、iOS、JavaScriptの3つのプロジェクトから構成されるため、更新が複雑になり得ると説明されています。

react-nativeとreactのバージョンを書き換えてnpm installが通ったとしても、それはスタート地点です。

ネイティブテンプレートとの差分が残っていないかを見る必要があります。

New Architectureを最後にONにする発想のまま進める

古い移行記事では「まず通常アップグレードして、最後にNew ArchitectureをONにする」という手順を見かけることがあります。

現在は前提が変わっています。

0.76でNew Architectureがデフォルトとなり、0.82からNew Architectureのみになりました。0.84以降はLegacy Architecture関連コードの削除も進んでいます。

そのため、0.87を移行先にするならNew Architecture対応は追加オプションではありません。

ライブラリを「インストールできるか」だけで判断する

npmでインストールできることと、現在のReact Nativeで安全に使い続けられることは同じではありません。

特にネイティブコードを持つライブラリでは、

メンテナンス状況
対象React Nativeへの対応
New Architecture対応
iOS側の対応
Android側の対応
代替ライブラリの有無

を見ます。

Interop Layerで現在動いている場合でも、それを長期的な設計判断と同一視しないほうがよいと思います。

複数の変更を一つのコミットへ入れる

アップグレード中に古いコードを見ると、ついリファクタリングしたくなります。

私も古いコードの作り直しに関わってきたので、この誘惑はよく分かります。

ただ、React Native更新、ライブラリ置換、画面のリファクタリング、状態管理変更を同時に行うと、問題が起きたときに原因を切り分けにくくなります。

アップグレード中は「きれいにする変更」と「動かすための変更」を分けます。

まず新しい環境で同じ挙動を作る。その後に整理するほうが、差分を追いやすくなります。

SimulatorとEmulatorだけで終わらせる

最終確認では実機も外せません。

通知、カメラ、ファイル、Deep Link、バックグラウンド処理など、OSとの接点がある機能は特に注意します。

またDebugビルドだけでなくRelease構成も確認します。

開発中に起動できることと、配布用アプリとして成立することは分けて考えたほうが安全です。

まとめ

数年前のReact Nativeアプリを現在へ戻す作業では、「何バージョン上げるか」だけを見ると全体を見失いやすくなります。

React Native 0.76以降のNew Architecture、0.82以降のNew Architectureのみの構成、0.87のStrict TypeScript APIや新しいツールチェーン要件など、数年間で前提そのものが変化しています。

私なら、次の順番で切り分けます。

現在地を記録する
↓
依存ライブラリを分類する
↓
自作ネイティブコードを探す
↓
Upgrade Helperでテンプレート差分を見る
↓
New Architecture対応を確認する
↓
TypeScript側の変更を確認する
↓
iOSとAndroidを別々にビルドする
↓
アプリ固有機能を実機で確認する

古いアプリの更新では、コードを書き始める前の棚卸しがかなり重要です。

React Native本体、ライブラリ、iOS、Android、アプリ固有コードを別々に見れば、「全部古い」という大きな問題を、確認可能な小さな問題へ分けられます。

その状態を作ってから、一つずつ新しい前提へ合わせる。

地味ですが、数年前のアプリを触るときほど、この進め方を大切にしたいと思っています。

参考文献

6
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?