新アーキテクチャについて
2018年以来、React Nativeチームは、開発者がより質の高いエクスペリエンスを作成できるように、React Nativeのコア内部を再設計してきました。2024年現在、このバージョンのReact Nativeは大規模に実証され、Metaの製品アプリを支えています。
新アーキテクチャという用語は、新しいフレームワークのアーキテクチャと、それをオープンソースに導入するための作業の両方を指します。
React Native 0.68以降、新アーキテクチャは実験的にオプトインできるようになり、その後の各リリースで継続的な改善が行われています。チームは現在、これをReact Nativeオープンソースエコシステムのデフォルトのエクスペリエンスにするために取り組んでいます。
なぜ新アーキテクチャなのか?
React Nativeで長年開発を続けてきた結果、チームは、開発者が特定の高度に洗練されたエクスペリエンスを作ることを妨げる一連の制約を特定しました。これらの制約はフレームワークの既存の設計に根ざしたものであったため、新アーキテクチャはReact Nativeの未来への投資として始まりました。
新アーキテクチャは、レガシーアーキテクチャでは不可能だった機能や改善を可能にします。
同期的なレイアウトとエフェクト
アダプティブなUIエクスペリエンスを構築するには、多くの場合、ビューのサイズと位置を測定し、レイアウトを調整する必要があります。
現在、ビューのレイアウト情報を取得して調整を行うには、onLayout
イベントを使用します。しかし、onLayout
コールバック内のstate更新は、前のレンダリングが描画された後に適用される可能性があります。これは、ユーザーが初期レイアウトのレンダリングとレイアウト測定への応答の間に、中間的な状態や視覚的なジャンプを目にする可能性があることを意味します。
新アーキテクチャでは、レイアウト情報への同期的なアクセスと、中間状態がユーザーに見えないように適切にスケジュールされた更新により、この問題を完全に回避できます。
例:ツールチップのレンダリング
ビューの上にツールチップを測定して配置することで、同期レンダリングがもたらす可能性を示すことができます。ツールチップは、どこにレンダリングすべきかを決定するために、ターゲットビューの位置を知る必要があります。
現在のアーキテクチャでは、onLayout
を使用してビューの測定値を取得し、そのビューの位置に基づいてツールチップの配置を更新します。
function ViewWithTooltip() {
// ...
// We get the layout information and pass to ToolTip to position itself
const onLayout = React.useCallback(event => {
targetRef.current?.measureInWindow((x, y, width, height) => {
// This state update is not guaranteed to run in the same commit
// This results in a visual "jump" as the ToolTip repositions itself
setTargetRect({x, y, width, height});
});
}, []);
return (
<>
<View ref={targetRef} onLayout={onLayout}>
<Text>Some content that renders a tooltip above</Text>
</View>
<Tooltip targetRect={targetRect} />
</>
);
}
新アーキテクチャでは、useLayoutEffect
を使用して、単一のコミットでレイアウトの測定と更新を同期的に適用し、視覚的な「ジャンプ」を回避できます。
function ViewWithTooltip() {
// ...
useLayoutEffect(() => {
// The measurement and state update for `targetRect` happens in a single commit
// allowing ToolTip to position itself without intermediate paints
targetRef.current?.measureInWindow((x, y, width, height) => {
setTargetRect({x, y, width, height});
});
}, [setTargetRect]);
return (
<>
<View ref={targetRef}>
<Text>Some content that renders a tooltip above</Text>
</View>
<Tooltip targetRect={targetRect} />
</>
);
}
Concurrent Rendererと各種機能のサポート
新アーキテクチャは、React 18以降でリリースされたConcurrent Rendering(並行レンダリング)と各種機能をサポートしています。これにより、データフェッチングのためのSuspenseやTransitions、その他の新しいReact APIをReact Nativeコードで使用できるようになり、WebとネイティブのReact開発間でのコードベースとコンセプトの一貫性がさらに高まります。
Concurrent Rendererはまた、自動バッチ処理のような、すぐに使える改善ももたらします。これによりReactでの再レンダリングが削減されます。
例:自動バッチ処理
新アーキテクチャでは、React 18レンダラーによる自動バッチ処理が利用できます。
この例では、スライダーでレンダリングするタイルの数を指定します。スライダーを0から1000にドラッグすると、stateの更新と再レンダリングが連続して高速に発生します。
同じコードでレンダラーを比較すると、新しいレンダラーの方がUIがスムーズで、中間的なUI更新が少ないことが視覚的にわかります。このネイティブのスライダーコンポーネントのような、ネイティブイベントハンドラからのstate更新がバッチ処理されるようになりました。


Transitionsのような新しい並行機能は、UI更新の優先度を表現する力を与えてくれます。更新を低優先度としてマークすることで、Reactはより優先度の高い更新を処理するためにその更新のレンダリングを「中断」できることを伝え、重要な場面でのレスポンシブなユーザー体験を保証します。
例:startTransition
の使用
前の例を基に、トランジションが進行中のレンダリングを中断して新しいstate更新を処理する方法を示すことができます。
タイルの数のstate更新をstartTransition
でラップし、タイルのレンダリングが中断可能であることを示します。startTransition
は、トランジションが完了したことを知らせるisPending
フラグも提供します。
function TileSlider({value, onValueChange}) {
const [isPending, startTransition] = useTransition();
return (
<>
<View>
<Text>
Render {value} Tiles
</Text>
<ActivityIndicator animating={isPending} />
</View>
<Slider
value={1}
minimumValue={1}
maximumValue={1000}
step={1}
onValueChange={newValue => {
startTransition(() => {
onValueChange(newValue);
});
}}
/>
</>
);
}
function ManyTiles() {
const [value, setValue] = useState(1);
const tiles = generateTileViews(value);
return (
<TileSlider onValueChange={setValue} value={value} />
<View>
{tiles}
</View>
)
}
トランジション内で頻繁な更新を行うと、Reactはstateが古くなったとたんにレンダリングを中断するため、中間状態のレンダリングが少なくなることに気づくでしょう。比較として、トランジションなしの場合、より多くの中間状態がレンダリングされます。どちらの例も自動バッチ処理を使用していますが、トランジションは開発者に進行中のレンダリングをバッチ処理するためのさらなる力を与えます。
高速なJavaScript/ネイティブ間の連携
新アーキテクチャは、JavaScriptとネイティブ間の非同期ブリッジを廃止し、JavaScript Interface (JSI) に置き換えます。JSIは、JavaScriptがC++オブジェクトへの参照を保持し、その逆も可能にするインターフェースです。メモリ参照により、シリアライズのコストなしにメソッドを直接呼び出すことができます。
JSIは、React Nativeで人気のカメラライブラリであるVisionCameraがフレームをリアルタイムで処理することを可能にしています。一般的なフレームバッファは約30 MBで、これはフレームレートにもよりますが、毎秒約2 GBのデータ量に相当します。ブリッジのシリアライズコストと比較して、JSIはその量の連携データを容易に処理します。JSIは、データベース、画像、音声サンプルなど、他の複雑なインスタンスベースの型も公開できます。
新アーキテクチャでのJSIの採用により、すべてのネイティブ-JavaScript相互運用からこの種のシリアライズ作業がなくなります。これには、View
やText
のようなネイティブのコアコンポーネントの初期化や再レンダリングも含まれます。新アーキテクチャにおけるレンダリングパフォーマンスの調査と、測定された改善されたベンチマークについて詳しく読むことができます。
新アーキテクチャを有効にすることで何を期待できますか?
新アーキテクチャはこれらの機能や改善を可能にしますが、アプリやライブラリで新アーキテクチャを有効にしても、すぐにパフォーマンスやユーザー体験が向上するとは限りません。
例えば、同期的なレイアウトエフェクトや並行機能のような新しい機能を活用するには、コードのリファクタリングが必要になるかもしれません。JSIはJavaScriptとネイティブメモリ間のオーバーヘッドを最小限に抑えますが、データシリアライズがアプリのパフォーマンスのボトルネックでなかった可能性もあります。
アプリやライブラリで新アーキテクチャを有効にすることは、React Nativeの未来にオプトインすることです。
チームは、新アーキテクチャがもたらす新しい機能を積極的に研究・開発しています。例えば、Webとの整合性向上はMetaで活発に探求されている分野であり、React Nativeオープンソースエコシステムにも導入される予定です。
専用のdiscussions & proposalsリポジトリで進捗を追い、コントリビュートすることができます。
今日から新アーキテクチャを使うべきですか?
0.76では、新アーキテクチャはすべてのReact Nativeプロジェクトでデフォルトで有効になります。
うまく動作しないものを見つけた場合は、このテンプレートを使用してissueをオープンしてください。
何らかの理由で新アーキテクチャを使用できない場合は、オプトアウトすることも可能です。
Android
android/gradle.properties
ファイルを開きますnewArchEnabled
フラグをtrue
からfalse
に切り替えます
# Use this property to enable support to the new architecture.
# This will allow you to use TurboModules and the Fabric render in
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
-newArchEnabled=true
+newArchEnabled=false
iOS
ios/Podfile
ファイルを開きます- Podfileのメインスコープに
ENV['RCT_NEW_ARCH_ENABLED'] = '0'
を追加します(テンプレートのPodfile参照)
+ ENV['RCT_NEW_ARCH_ENABLED'] = '0'
# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
- 次のコマンドでCocoaPodsの依存関係をインストールします
bundle exec pod install