新しいアーキテクチャについて
2018年以来、React Nativeチームは、開発者がより高品質な体験を作成できるように、React Nativeのコア内部を再設計してきました。2024年現在、このバージョンのReact Nativeは大規模に実証されており、Metaによるプロダクションアプリを動かしています。
新しいアーキテクチャという用語は、新しいフレームワークアーキテクチャと、それをオープンソースに持ち込むための作業の両方を指します。
新しいアーキテクチャは、React Native 0.68から実験的なオプトインとして利用可能になり、以降のすべてのリリースで継続的に改善されています。チームは現在、これをReact Nativeオープンソースエコシステムにおけるデフォルトのエクスペリエンスにすることに取り組んでいます。
新しいアーキテクチャが必要な理由
React Nativeでの長年の開発を経て、チームは、開発者が高度な洗練された特定の体験を作成することを妨げる一連の制限を特定しました。これらの制限は、フレームワークの既存のデザインにとって根本的なものであったため、新しいアーキテクチャはReact Nativeの将来への投資として始まりました。
新しいアーキテクチャは、従来のアーキテクチャでは不可能だった機能と改善を解放します。
同期レイアウトとエフェクト
適応型のUIエクスペリエンスを構築するには、多くの場合、ビューのサイズと位置を測定し、レイアウトを調整する必要があります。
現在、ビューのレイアウト情報を取得し、調整を行うには、onLayout
イベントを使用します。ただし、onLayout
コールバック内の状態更新は、前のレンダリングの描画後に適用される場合があります。これは、ユーザーが初期レイアウトのレンダリングとレイアウト測定への応答の間で、中間状態や視覚的なジャンプを見る可能性があることを意味します。
新しいアーキテクチャでは、レイアウト情報への同期アクセスと、中間状態がユーザーに見えないように適切にスケジュールされた更新により、この問題を完全に回避できます。
例:ツールチップのレンダリング
ビューの上にツールチップを測定して配置することで、同期レンダリングが解放されるものを紹介できます。ツールチップは、どこにレンダリングする必要があるかを判断するために、ターゲットビューの位置を知る必要があります。
現在のアーキテクチャでは、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
を使用して、1回のコミットで同期的に測定してレイアウト更新を適用し、視覚的な「ジャンプ」を回避できます。
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} />
</>
);
}
同時レンダラーと機能のサポート
新しいアーキテクチャは、React 18以降で出荷された同時レンダリングと機能をサポートしています。React Nativeコードで、データフェッチのためのSuspense、Transitions、その他の新しいReact APIなどの機能を使用できるようになり、WebとネイティブReact開発の間でコードベースと概念がさらに適合します。
同時レンダラーは、Reactでの再レンダリングを減らす自動バッチ処理など、すぐに使える改善ももたらします。
例:自動バッチ処理
新しいアーキテクチャでは、React 18レンダラーで自動バッチ処理が行われます。
この例では、スライダーはレンダリングするタイルの数を指定します。スライダーを0から1000にドラッグすると、一連の状態更新と再レンダリングがすばやく発生します。
同じコードのレンダラーを比較すると、レンダラーが、中間UI更新が少なく、よりスムーズなUIを提供していることに視覚的に気づくことができます。このネイティブスライダーコンポーネントのようなネイティブイベントハンドラーからの状態更新が、バッチ処理されるようになりました。


Transitionsのような新しい同時機能を使用すると、UI更新の優先度を表現できます。更新を優先度の低いものとしてマークすると、Reactは、より優先度の高い更新を処理するために更新のレンダリングを「中断」して、必要な場所で応答性の高いユーザーエクスペリエンスを確保できることを意味します。
例:startTransition
の使用
前の例を基にして、トランジションが、新しい状態更新を処理するために、進行中のレンダリングを中断する方法を紹介できます。
タイルをレンダリングすると中断できることを示すために、タイル番号の状態更新を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は、状態が古くなるとすぐに状態のレンダリングを中止するため、レンダリングされる中間状態が少なくなることに気づくでしょう。比較すると、トランジションがない場合は、より多くの中間状態がレンダリングされます。どちらの例でも、自動バッチ処理が使用されています。それでも、トランジションは、進行中のレンダリングをバッチ処理する開発者にとって、さらに強力な機能を提供します。
高速なJavaScript/ネイティブインターフェース
新しいアーキテクチャは、JavaScriptとネイティブ間の非同期ブリッジを削除し、JavaScript Interface(JSI)に置き換えます。JSIは、JavaScriptがC++オブジェクトへの参照を保持し、その逆も可能にするインターフェースです。メモリ参照を使用すると、シリアル化コストなしでメソッドを直接呼び出すことができます。
JSIを使用すると、React Nativeの一般的なカメラライブラリであるVisionCameraは、リアルタイムでフレームを処理できます。一般的なフレームバッファは10MBで、フレームレートに応じて1秒あたり約1GBのデータになります。ブリッジのシリアル化コストと比較して、JSIはその量のインターフェースデータを簡単に処理します。JSIは、データベース、画像、オーディオサンプルなど、他の複雑なインスタンスベースの型を公開できます。
新しいアーキテクチャでのJSIの採用により、このクラスのシリアル化作業が、すべてのネイティブ-JavaScript相互運用から削除されます。これには、View
やText
のようなネイティブコアコンポーネントの初期化と再レンダリングが含まれます。新しいアーキテクチャでのレンダリングパフォーマンスの調査と、測定した改善されたベンチマークについて詳しく読むことができます。
新しいアーキテクチャを有効にすると何が期待できますか?
新しいアーキテクチャはこれらの機能と改善を有効にしますが、アプリまたはライブラリで新しいアーキテクチャを有効にしても、パフォーマンスやユーザーエクスペリエンスがすぐに向上するとは限りません。
たとえば、同期レイアウトエフェクトや同時機能のような新しい機能を活用するには、コードのリファクタリングが必要になる場合があります。JSIはJavaScriptとネイティブメモリ間のオーバーヘッドを最小限に抑えますが、データシリアル化がアプリのパフォーマンスのボトルネックになっていない可能性があります。
アプリまたはライブラリで新しいアーキテクチャを有効にすることは、React Nativeの未来にオプトインすることです。
チームは、新しいアーキテクチャが解放する新しい機能を積極的に研究および開発しています。たとえば、Webの調整はMetaでのアクティブな調査分野であり、React Nativeオープンソースエコシステムに出荷されます。
専用のディスカッションと提案リポジトリで、フォローして貢献することができます。
今すぐ新しいアーキテクチャを使用する必要がありますか?
0.76では、新しいアーキテクチャはすべてのReact Nativeプロジェクトでデフォルトで有効になっています。
うまく機能しないものを見つけた場合は、このテンプレートを使用して問題をオープンしてください。
何らかの理由で新しいアーキテクチャを使用できない場合は、オプトアウトすることもできます
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