New Architectureをデフォルトで搭載したReact Native 0.76がnpmで利用可能になりました!0.76リリースブログ記事では、このバージョンに含まれる重要な変更点のリストを共有しました。この記事では、New ArchitectureとそれがReact Nativeの未来をどのように形作るかについて概要を説明します。
New Architectureは、Suspense、Transitions、自動バッチング、useLayoutEffectを含む、モダンなReact機能の完全サポートを追加します。New Architectureには、ブリッジを介さずにネイティブインターフェースへの直接アクセスでタイプセーフなコードを書けるようにする新しいNative ModuleとNative Componentシステムも含まれています。
このリリースは、2018年から取り組んできたReact Nativeの根本的な書き直しの結果であり、New Architectureをほとんどのアプリにとって段階的な移行にするために特別な配慮を行いました。2021年には、React エコシステム全体でスムーズなアップグレード体験を確保するためにコミュニティと協力するNew Architecture Working Groupを設立しました。
ほとんどのアプリは、他のリリースと同じレベルの労力でReact Native 0.76を採用できるでしょう。最も人気のあるReact Nativeライブラリは既にNew Architectureをサポートしています。New Architectureには、古いアーキテクチャを対象とするライブラリとの後方互換性を可能にする自動相互運用レイヤーも含まれています。
過去数年間の開発において、私たちのチームはNew Architectureのビジョンを公開で共有してきました。これらの講演を見逃した場合は、こちらでご確認ください:
- React Native EU 2019 - The New React Native
- React Conf 2021 - React 18 Keynote
- App.js 2022 - Bringing the New React Native Architecture to the OSS Community
- React Conf 2024 - Day 2 Keynote
New Architectureとは
New Architectureは、コンポーネントのレンダリング方法、JavaScript抽象化とネイティブ抽象化の通信方法、異なるスレッド間での作業スケジューリング方法を含む、React Nativeを支える主要システムの完全な書き直しです。ほとんどのユーザーはこれらのシステムがどのように動作するかを考える必要はありませんが、これらの変更は改善と新しい機能をもたらします。
古いアーキテクチャでは、React Nativeは非同期ブリッジを使用してネイティブプラットフォームと通信していました。コンポーネントをレンダリングしたりネイティブ関数を呼び出すために、React Nativeはネイティブ関数呼び出しをシリアライズしてブリッジでキューに入れる必要があり、これは非同期で処理されていました。
このアーキテクチャの利点は、すべての作業がバックグラウンドスレッドで行われるため、レンダリング更新やネイティブモジュール関数呼び出しの処理でメインスレッドがブロックされることがなかったことです。しかし、ユーザーはネイティブアプリのような感覚を得るために、インタラクションに対する即座のフィードバックを期待します。これは、一部の更新がユーザー入力に応答して同期的にレンダリングされる必要があり、進行中のレンダリングを中断する可能性があることを意味します。
古いアーキテクチャは非同期のみだったため、非同期と同期の両方の更新を可能にするために書き直す必要がありました。さらに、古いアーキテクチャでは、ブリッジ上での関数呼び出しのシリアライズが、特に頻繁な更新や大きなオブジェクトに対してボトルネックになりました。これにより、アプリが確実に60+ FPSを達成することが困難になりました。
同期の問題もありました:JavaScriptとネイティブレイヤーが同期を失った場合、それらを同期的に調整することは不可能で、リストが空白フレームを表示したり、中間状態のレンダリングによる視覚的なUIジャンプなどのバグが発生しました。
最後に、古いアーキテクチャはネイティブ階層を使用してUIの単一コピーを保持し、そのコピーをその場で変更していたため、レイアウトは単一スレッドでのみ計算できました。これにより、ユーザー入力などの緊急更新を処理することが不可能になり、ツールチップの位置を更新するためにレイアウトエフェクトで読み取るなど、レイアウトを同期的に読み取ることができませんでした。
これらすべての問題により、Reactの並行機能を適切にサポートすることは不可能でした。
これらの問題を解決するために、New Architectureには4つの主要部分が含まれています:
- New Native Module System
- New Renderer
- Event Loop
- ブリッジの削除
New Module systemにより、React Native Rendererはネイティブレイヤーへの同期アクセスが可能になり、イベントの処理、更新のスケジューリング、レイアウトの読み取りを非同期と同期の両方で行えるようになります。新しいNative Moduleはデフォルトで遅延読み込みされ、アプリに大幅なパフォーマンス向上をもたらします。
New Rendererは複数のスレッドにわたって複数の進行中ツリーを処理でき、Reactがメインスレッドまたはバックグラウンドスレッドで複数の並行更新優先度を処理できるようになります。また、ジャンクのないより応答性の高いUIをサポートするために、複数のスレッドから同期的または非同期的にレイアウトを読み取ることもサポートします。
新しいEvent LoopはJavaScriptスレッド上のタスクを明確に定義された順序で処理できます。これにより、Reactはレンダリングを中断してイベントを処理できるため、緊急のユーザーイベントが低優先度のUIトランジションより優先されます。Event Loopはウェブ仕様とも整合しているため、microtasks、MutationObserver、IntersectionObserverなどのブラウザ機能をサポートできます。
最後に、ブリッジを削除することで、より高速な起動とJavaScriptとネイティブランタイム間の直接通信が可能になり、作業切り替えのコストが最小化されます。これにより、より良いエラー報告、デバッグ、未定義動作によるクラッシュの削減も可能になります。
New Architectureは本番環境で使用する準備が整いました。MetaのFacebookアプリや他の製品で既に大規模に使用されています。QuestデバイスのFacebookとInstagramアプリの開発でReact NativeとNew Architectureを成功裏に使用しました。パートナーは既に数ヶ月間本番環境でNew Architectureを使用しています:ExpensifyとKrakenの成功事例をご覧いただき、Blueskyの新リリースもお試しください。
New Native Modules
New Native Module SystemはJavaScriptとネイティブプラットフォームの通信方法の大幅な書き直しです。完全にC++で書かれており、多くの新しい機能を解放します:
- ネイティブランタイムとの同期アクセス
- JavaScriptとネイティブコード間のタイプセーフティ
- プラットフォーム間でのコード共有
- デフォルトでの遅延モジュール読み込み
新しいNative Moduleシステムでは、JavaScriptとネイティブレイヤーがJavaScript Interface(JSI)を通じて非同期ブリッジを使用せずに同期的に通信できるようになりました。これは、カスタムNative Moduleが関数を同期的に呼び出し、値を返し、その値を別のNative Module関数に渡すことができることを意味します。
古いアーキテクチャでは、ネイティブ関数呼び出しからの応答を処理するためにコールバックを提供する必要があり、返される値はシリアライズ可能である必要がありました:
nativeModule.getValue(value => {
nativeModule.doSomething(value);
});
New Architectureでは、ネイティブ関数への同期呼び出しが可能です:
const value = nativeModule.getValue();
nativeModule.doSomething(value);
New Architectureにより、JavaScript/TypeScript APIからアクセスしながら、C++ネイティブ実装の完全な力を最終的に活用できます。
New Module SystemはC++で書かれたモジュールをサポートしているため、モジュールを一度書けば、Android、iOS、Windows、macOSを含むすべてのプラットフォームで動作します。C++でモジュールを実装することで、より細かいメモリ管理とパフォーマンス最適化が可能になります。
さらに、Codegenにより、モジュールはJavaScriptレイヤーとネイティブレイヤー間の強く型付けされた契約を定義できます。私たちの経験では、境界を越えた型エラーはクロスプラットフォームアプリでのクラッシュの最も一般的な原因の一つです。Codegenにより、ボイラープレートコードを生成しながらこれらの問題を克服できます。
最後に、モジュールは遅延読み込みされるようになりました:起動時ではなく、実際に必要な時にのみメモリに読み込まれます。これにより、アプリの起動時間が短縮され、アプリケーションが複雑になっても低く保たれます。
react-native-mmkvなどの人気ライブラリは、新しいNative Moduleへの移行から既に恩恵を受けています:
「新しいNative Moduleは、react-native-mmkvのセットアップ、オートリンク、初期化を大幅に簡素化しました。New Architectureのおかげで、react-native-mmkvは純粋なC++ Native Moduleになり、どのプラットフォームでも動作できるようになりました。新しいCodegenによりMMKVは完全にタイプセーフになり、null安全性を強制することで長年のNullPointerReference問題が修正され、Native Module関数を同期的に呼び出せることで、カスタムJSIアクセスを新しいNative Module APIに置き換えることができました。」
— Marc Rousavy、react-native-mmkvの作成者
New Renderer
Native Rendererも完全に書き直し、いくつかの利点を追加しました:
- 更新を異なる優先度で異なるスレッドでレンダリング可能
- レイアウトを同期的かつ異なるスレッド間で読み取り可能
- レンダラーはC++で書かれ、すべてのプラットフォーム間で共有
更新されたNative Rendererは、ビュー階層を不変ツリー構造で保存するようになりました。これは、UIが直接変更できない方法で保存されることを意味し、更新のスレッドセーフな処理を可能にします。これにより、それぞれがユーザーインターフェースの異なるバージョンを表す複数の進行中ツリーを処理できます。
その結果、更新は(トランジション中などの)UIをブロックせずにバックグラウンドで、または(ユーザー入力に応答して)メインスレッドでレンダリングできます。複数のスレッドをサポートすることで、Reactは低優先度の更新を中断してユーザー入力によって生成されるような緊急の更新をレンダリングし、必要に応じて低優先度の更新を再開できます。
新しいレンダラーは、レイアウト情報を同期的かつ異なるスレッド間で読み取ることもできます。これにより、低優先度更新のバックグラウンド計算と、ツールチップの再配置などの必要時の同期読み取りが可能になります。
最後に、レンダラーをC++で書き直すことで、すべてのプラットフォーム間で共有できるようになります。これにより、iOS、Android、Windows、macOS、その他のReact Nativeサポートプラットフォームで同じコードが実行され、各プラットフォームでの再実装を必要とせずに一貫したレンダリング機能を提供します。これは、私たちのMany Platform Visionに向けた重要な一歩です。
例えば、View FlatteningはAndroidのみの最適化で、深いレイアウトツリーを避けるためのものでした。共有C++コアを持つ新しいレンダラーは、この機能をiOSにもたらします。この最適化は自動的で、セットアップは不要で、共有レンダラーで無料で提供されます。
これらの変更により、React NativeはSuspenseやTransitionsなどのConcurrent React機能を完全にサポートし、ジャンク、遅延、視覚的ジャンプなしにユーザー入力に素早く応答する複雑なユーザーインターフェースの構築が容易になります。
将来的には、これらの新しい機能を活用してFlatListやTextInputなどの組み込みコンポーネントにさらなる改善をもたらす予定です。
Reanimatedなどの人気ライブラリは既にNew Rendererを活用しています:
「現在開発中のReanimated 4は、New Rendererと直接連携する新しいアニメーションエンジンを導入し、アニメーションを処理し、異なるスレッド間でレイアウトを管理できるようにします。New Rendererの設計により、多数の回避策に依存せずにこれらの機能を構築することが真に可能になります。さらに、C++で実装され、プラットフォーム間で共有されるため、Reanimatedの大部分を一度書くことができ、プラットフォーム固有の問題を削減し、コードベースを最小化し、ツリー外プラットフォームでの採用を合理化します。」
— Krzysztof Magiera、Reanimatedの作成者
Event Loop
New Architectureにより、このRFCで説明されているように、明確に定義されたイベントループ処理モデルを実装できました。このRFCはHTML Standardで説明されている仕様に従い、React NativeがJavaScriptスレッドでタスクを実行する方法を説明しています。
明確に定義されたイベントループの実装により、React DOMとReact Native間のギャップが埋まります:React Nativeアプリケーションの動作がReact DOMアプリケーションの動作により近くなり、一度学んで書くことが容易になります。