Claude Media
Claude CodeでReact Native移行のNew Architecture非対応ライブラリを洗い出す

Claude CodeでReact Native移行のNew Architecture非対応ライブラリを洗い出す

package.jsonのネイティブ依存をreactnative.directoryで仕分け、判定できない分だけ読み取り専用のサブエージェントに調べさせる手順です。

React NativeのNew Architecture移行で最初に詰まるのは、自分のコードではなく依存ライブラリです。package.json に並ぶ数十のパッケージのうち、ネイティブコードを持つものだけが対象になり、その一つ一つが新アーキテクチャ(FabricとTurboModules)で動くかを確かめなければなりません。

この記事では、依存の抽出と一次判定をスクリプトに任せ、判定が付かなかったものだけを読み取り専用のサブエージェントに調べさせる流れを組みます。Claude Codeの役割は「証拠を集めて仕分けた表を返すこと」に絞り、アップグレードや置き換えの判断は人が持つ構成です。新規アプリの実機ビルドまではClaude CodeでReact Nativeアプリを実機ビルドまで作る手順で扱っているので、ここでは既存アプリの移行に話を絞ります。

まず確認する:自分のReact Nativeバージョンで何が選べるか

移行の進め方は、現在のReact Nativeバージョンで変わります。ここを外すと、洗い出しの結果の使い方が逆になります。

React Native 0.76でNew Architectureが既定になりました。0.82ではNew Architectureが唯一のアーキテクチャになり、Androidの newArchEnabled=false やiOSの RCT_NEW_ARCH_ENABLED=0 は無視されます。公式のリリース記事は、まだ移行していないプロジェクトにはReact Native 0.81かExpo SDK 54でNew Architectureを有効にして動作を確かめ、そのあとで0.82へ上げる順序を勧めています。0.81とExpo SDK 54は、レガシーアーキテクチャを使える最後のバージョンです。

くらべる

バージョンごとの選択肢

レガシーに戻せる

0.81以前

newArchEnabled と RCT_NEW_ARCH_ENABLED で切り替えられます。非対応ライブラリが見つかっても、いったん無効に戻して調査を続けられます。

戻せない

0.82以降

New Architectureのみで動きます。非対応ライブラリは、上げる・置き換える・メンテナに依頼するのどれかで解消することになります。

公式ドキュメントの「About the New Architecture」ページは、更新日が2026年10月14日と新しいにもかかわらず、gradle.properties や Podfile でオプトアウトする手順を載せたままです。0.82のリリース記事と並べると食い違って見えるため、0.82以降のプロジェクトでは後者に従い、オプトアウト手順は0.81以前向けと読むのが安全です。

非対応と判定する前に知っておく互換レイヤー

「New Architecture非対応」と一口に言っても、動かないとは限りません。0.76のリリース記事によると、New Architectureには旧アーキテクチャ向けライブラリのための相互運用レイヤーが自動で入り、多くのアプリは変更なしで動きます。既知の制限として、カスタムのShadow Nodeへのアクセスと並行機能(concurrent features)には対応しません。

0.82の記事は、この相互運用レイヤーを当面コードベースに残す方針を示しています。

つまり洗い出しの目的は「動かないものを列挙する」ことより、「公式に対応を表明しているもの」と「互換レイヤー頼みのもの」を分けることです。後者は動いていても、レイヤーが外れる日が来たときに最初に壊れる候補になります。

ステップ1:ネイティブ依存を抜き出して公式ディレクトリで一次判定する

ネイティブコードを持つ依存かどうかは、node_modules の中に android ディレクトリ、ios ディレクトリ、.podspec のいずれかがあるかで見分けられます。純粋なJavaScriptのパッケージ(lodash など)はこの検査で外れます。

対応状況の問い合わせ先は、React Nativeの公式リリース記事が互換性の確認先として挙げているreactnative.directoryです。サイトの検索が内部で使うJSON APIの応答には、各ライブラリの newArchitecture が含まれます。応答では true、'new-arch-only'、false のいずれかが入り、項目そのものが無いこともあります。

次のスクリプトは、この二つを組み合わせます。プロジェクトのルートに rn-newarch-triage.mjs として置き、node_modules がある状態で実行します。

import { readFileSync, existsSync, readdirSync } from 'node:fs';
 
const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
const deps = Object.keys({ ...pkg.dependencies, ...pkg.devDependencies });
 
// android/ios ディレクトリか podspec があればネイティブ依存とみなす
const isNative = (name) => {
  const dir = `node_modules/${name}`;
  if (!existsSync(dir)) return false;
  return (
    existsSync(`${dir}/android`) ||
    existsSync(`${dir}/ios`) ||
    readdirSync(dir).some((f) => f.endsWith('.podspec'))
  );
};
 
const rows = [];
for (const name of deps.filter(isNative)) {
  const res = await fetch(
    `https://reactnative.directory/api/libraries?search=${encodeURIComponent(name)}&limit=20`,
  );
  const { libraries = [] } = await res.json();
  // 検索はあいまい一致。npmPkg が完全一致する行だけ採用する
  const hit = libraries.find((l) => l.npmPkg === name);
  const status = !hit
    ? 'not-listed'
    : hit.newArchitecture === true
      ? 'supported'
      : hit.newArchitecture === 'new-arch-only'
        ? 'new-arch-only'
        : hit.newArchitecture === false
          ? 'unsupported-listed'
          : 'unknown';
  rows.push({ name, version: pkg.dependencies?.[name] ?? pkg.devDependencies?.[name], status });
}
console.table(rows);
node rn-newarch-triage.mjs

手元の最小構成(react-native-mmkv と、ios ディレクトリを持つ react-native-camera を置いたプロジェクト)で動かすと、react-native-mmkv は supported、react-native-camera は not-listed と出ました。react-native-camera を検索したところ、返ってきた20件以内に npmPkg が完全一致する行がなかったためです。

完全一致で絞っているのには理由があります。あいまい一致のまま先頭の1件を採用すると、react-native-camera の検索で react-native-camera-kit の true を拾い、別のライブラリの結果を自分の依存に当てはめてしまいます。

5つの状態をどう読むか

status意味次にやること
supported意味ディレクトリ上で対応済み次にやること追加調査なし。メジャーバージョンが現在の依存と合っているかだけ見る
new-arch-only意味新アーキテクチャ専用のライブラリ次にやること0.82以降なら問題なし。0.81以前ならレガシー併用時に動かない
unsupported-listed意味ディレクトリが非対応と明示(false)次にやることステップ2で代替やアップグレード先、互換レイヤーで動くかを確認する
unknown意味一覧にあるが newArchitecture の項目が無い次にやることステップ2でサブエージェントに調べさせる
not-listed意味完全一致する行がない次にやること同じくステップ2へ。ディレクトリ未登録と非対応は別物

unknown と not-listed は「非対応」ではなく「ディレクトリからは判定できない」という状態です。この二つを非対応として扱うと、実際には対応済みのライブラリを置き換えてしまう手戻りが出ます。

ステップ2:判定できなかった依存をサブエージェントに調べさせる

unknown と not-listed の依存は、ライブラリ自身のコードと更新履歴に証拠があります。数が多いと一件ずつ調べるだけで会話のコンテキストが埋まるため、調査をサブエージェントに切り出します。サブエージェントの公式ドキュメントも、出力量の大きい調査を別コンテキストに隔離する使い方と、独立した調査を並列に走らせる使い方を紹介しています。

調査役の定義ファイル

プロジェクトの .claude/agents/ に次のファイルを置きます。書き込み系のツールを与えず、調べるだけの役にします。

---
name: rn-newarch-checker
description: React Nativeライブラリ1つがNew Architecture(Fabric / TurboModules)に対応しているかを、node_modules内のコードと公開情報の証拠から調べる。依存の仕分けを頼まれたときに使う
tools: Read, Grep, Glob, WebFetch
model: sonnet
maxTurns: 15
---
 
調査対象のnpmパッケージ名は依頼文で渡される。次の順で証拠を集める。
 
1. node_modules/<パッケージ>/package.json に codegenConfig があるか
2. ソースに Native*.ts の spec があるか。TurboModuleRegistry や
   codegenNativeComponent を使っているか
3. README と変更履歴に New Architecture / Fabric / TurboModules の記載があるか
4. リポジトリの最終更新時期と、メンテナンス終了の表明があるか
 
最後に次の1行形式だけを返す。推測は書かない。
 
<パッケージ名> | supported / interop-only / unsupported / unclear | 根拠となるファイルや記述の引用

tools に列挙したツールだけが使えます。name と description は必須で、tools を省くと利用可能な全ツールを引き継ぐため、読み取り専用にしたいときは明示するのが確実です。maxTurns を付けると、調査が長引いたときに上限で止まり、部分的な出力が返ります。

並列に走らせて、1行ずつ集める

メインの会話では、判定できなかった依存をまとめて渡します。

rn-newarch-triage.mjs の結果で unknown と not-listed になった依存を、
rn-newarch-checker で1パッケージずつ並列に調べて。
結果は表にまとめ、根拠の引用つきで返して。

サブエージェントの出力は1行に絞ってあるので、メインの会話に戻るのは依存の数だけの短い行です。公式ドキュメントは、詳細な結果を返すサブエージェントを大量に走らせるとコンテキストを消費すると注意しています。出力形式を1行に固定する理由はここにあります。

同時に動かせるサブエージェントは、既定で20個までです。上限を超えると Concurrent subagent limit reached で失敗しますが、依存が数十個あっても、20個前後に分けて依頼すれば問題になりません。上限を変えるときは CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS を設定します。

証拠の読み方:新アーキテクチャの目印

調査役が探す目印は、公式のCodegenとTurbo Native Modulesのドキュメントに根拠があります。

  • package.json の codegenConfig フィールド:Codegenの設定を持つライブラリで、モジュールやコンポーネントの型定義から接続コードを生成する
  • NativeLocalStorage.ts のようなspecファイルと、TurboModuleRegistry の利用:Turbo Native Moduleの書き方
  • codegenNativeComponent の利用:Fabric Native Componentの書き方

これらが見つかれば supported の有力な根拠になります。見つからない場合は unsupported ではなく interop-only か unclear に倒します。前述の相互運用レイヤーのおかげで、目印のない旧来のライブラリも多くが動くからです。定義ファイルの最終行の形式に interop-only を入れてあるのは、そのためです。

ステップ3:結果を表にして、次の手を決める

サブエージェントが返した行とステップ1の結果をまとめると、依存は4つのグループに分かれます。

  • 対応済み(supported、new-arch-only):手を入れない
  • interop-only:動作確認の対象。新アーキテクチャで該当画面を動かし、問題がなければ当面そのまま
  • unsupported:アップグレード先の有無、代替ライブラリ、メンテナへの依頼の順に検討する
  • unclear:人が一次情報を読む

対処の選択肢は、公式のリリース記事に沿って整理できます。互換性のない依存で止まっている場合、0.82の記事はライブラリのメンテナへ直接連絡することを勧めています。React Native本体のバグが原因なら、課題トラッカーに報告します。0.76の記事には、メンテナンスされていないライブラリなら同じ機能を持つ別のライブラリを検討するという案も載っています。

アップグレードや置き換えまでClaude Codeに任せる場合も、1パッケージずつ別のコミットに分けると、問題が出たときに戻す範囲が小さくなります。調査だけを読み取り専用の役に分けておくと、判断の前に「証拠の一覧」が手元に残ります。

調査を安全に回すためのサブエージェントの権限設計は、Claude Codeのサブエージェントをhookで読み取り専用のDB分析役にするの手順がそのまま使えます。調査の重さに合わせてモデルの思考量を変えたい場合は、Claude Codeのサブエージェントをeffort指定で走らせる方法が参考になります。

よくあるつまずき

  • 項目なしを非対応と読む:newArchitecture の項目が無いのは「記載がない」状態で、false(非対応と明示)とは別です。混同しないよう、スクリプトは両者を別のstatusに分けています。実際に動くかはステップ2で確かめます
  • 検索結果の先頭を採用する:検索はあいまい一致で、似た名前の別ライブラリが先頭に来ます。npmPkg の完全一致で絞ります
  • node_modules なしで空表になる:ネイティブ依存の判定は node_modules の中身を見ます。npm install や yarn install を済ませてから実行します
  • モノレポで依存が見つからない:パッケージが巻き上げられて、ルートの node_modules にだけ存在することがあります。ワークスペースごとに実行するか、node_modules の探索先を調整します
  • サブエージェントが調査対象を勝手に変更する:tools を省くと、利用可能な全ツールを引き継ぎます。調査役の定義には必ず tools を書きます
  • 0.82以降でオプトアウトを試す:newArchEnabled=false は無視されます。非対応ライブラリで止まったときは、上げる・置き換える・メンテナに連絡するのどれかで進めます

まとめ

洗い出しの中心は、ディレクトリの記載を「対応済み」と「判定不能」に分け、判定不能を証拠で埋めることです。判定不能の依存は、非対応ではなく互換レイヤーで動いているだけのことが多く、置き換えに進む前に動作確認と証拠の確認を挟めば手戻りが減ります。Claude Codeの調査役は、結論を出す役ではなく、根拠つきで一覧を作る役として使うと、あとから見直せる記録が残ります。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/sub-agents
  2. reactnative.dev/architecture/landing-page
  3. reactnative.dev/blog/2025/10/08/react-native-0.82
  4. reactnative.dev/blog/2024/10/23/the-new-architecture-is-here
  5. reactnative.dev/docs/the-new-architecture/using-codegen
  6. reactnative.directory/
この記事を共有:XはてブLinkedIn