メインコンテンツへスキップ

新アーキテクチャについて

2018年以来、React Nativeチームは、開発者がより質の高いエクスペリエンスを作成できるように、React Nativeのコア内部を再設計してきました。2024年現在、このバージョンのReact Nativeは大規模に実証され、Metaの製品アプリを支えています。

新アーキテクチャという用語は、新しいフレームワークのアーキテクチャと、それをオープンソースに導入するための作業の両方を指します。

React Native 0.68以降、新アーキテクチャは実験的にオプトインできるようになり、その後の各リリースで継続的な改善が行われています。チームは現在、これをReact Nativeオープンソースエコシステムのデフォルトのエクスペリエンスにするために取り組んでいます。

なぜ新アーキテクチャなのか?

React Nativeで長年開発を続けてきた結果、チームは、開発者が特定の高度に洗練されたエクスペリエンスを作ることを妨げる一連の制約を特定しました。これらの制約はフレームワークの既存の設計に根ざしたものであったため、新アーキテクチャはReact Nativeの未来への投資として始まりました。

新アーキテクチャは、レガシーアーキテクチャでは不可能だった機能や改善を可能にします。

同期的なレイアウトとエフェクト

アダプティブなUIエクスペリエンスを構築するには、多くの場合、ビューのサイズと位置を測定し、レイアウトを調整する必要があります。

現在、ビューのレイアウト情報を取得して調整を行うには、onLayoutイベントを使用します。しかし、onLayoutコールバック内のstate更新は、前のレンダリングが描画された後に適用される可能性があります。これは、ユーザーが初期レイアウトのレンダリングとレイアウト測定への応答の間に、中間的な状態や視覚的なジャンプを目にする可能性があることを意味します。

新アーキテクチャでは、レイアウト情報への同期的なアクセスと、中間状態がユーザーに見えないように適切にスケジュールされた更新により、この問題を完全に回避できます。

例:ツールチップのレンダリング

ビューの上にツールチップを測定して配置することで、同期レンダリングがもたらす可能性を示すことができます。ツールチップは、どこにレンダリングすべきかを決定するために、ターゲットビューの位置を知る必要があります。

現在のアーキテクチャでは、onLayoutを使用してビューの測定値を取得し、そのビューの位置に基づいてツールチップの配置を更新します。

jsx
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を使用して、単一のコミットでレイアウトの測定と更新を同期的に適用し、視覚的な「ジャンプ」を回避できます。

jsx
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} />
</>
);
}
A view that is moving to the corners of the viewport and center with a tooltip rendered either above or below it. The tooltip is rendered after a short delay after the view moves
ツールチップの非同期な測定とレンダリング。コードを見る
A view that is moving to the corners of the viewport and center with a tooltip rendered either above or below it. The view and tooltip move in unison.
ツールチップの同期的な測定とレンダリング。コードを見る

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更新がバッチ処理されるようになりました。

A video demonstrating an app rendering many views according to a slider input. The slider value is adjusted from 0 to 1000 and the UI slowly catches up to rendering 1000 views.
レガシーレンダラーでの頻繁なstate更新のレンダリング。
A video demonstrating an app rendering many views according to a slider input. The slider value is adjusted from 0 to 1000 and the UI resolves to 1000 views faster than the previous example, without as many intermediate states.
React 18レンダラーでの頻繁なstate更新のレンダリング。

Transitionsのような新しい並行機能は、UI更新の優先度を表現する力を与えてくれます。更新を低優先度としてマークすることで、Reactはより優先度の高い更新を処理するためにその更新のレンダリングを「中断」できることを伝え、重要な場面でのレスポンシブなユーザー体験を保証します。

例:startTransitionの使用

前の例を基に、トランジションが進行中のレンダリングを中断して新しいstate更新を処理する方法を示すことができます。

タイルの数のstate更新をstartTransitionでラップし、タイルのレンダリングが中断可能であることを示します。startTransitionは、トランジションが完了したことを知らせるisPendingフラグも提供します。

jsx
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が古くなったとたんにレンダリングを中断するため、中間状態のレンダリングが少なくなることに気づくでしょう。比較として、トランジションなしの場合、より多くの中間状態がレンダリングされます。どちらの例も自動バッチ処理を使用していますが、トランジションは開発者に進行中のレンダリングをバッチ処理するためのさらなる力を与えます。

A video demonstrating an app rendering many views (tiles) according to a slider input. The views are rendered in batches as the slider is quickly adjusted from 0 to 1000. There are less batch renders in comparison to the next video.
トランジションを使用して、古いstateの進行中のレンダリングを中断しながらタイルをレンダリングする。コードを見る
A video demonstrating an app rendering many views (tiles) according to a slider input. The views are rendered in batches as the slider is quickly adjusted from 0 to 1000.
トランジションとしてマークせずにタイルをレンダリングする。コードを見る

高速なJavaScript/ネイティブ間の連携

新アーキテクチャは、JavaScriptとネイティブ間の非同期ブリッジを廃止し、JavaScript Interface (JSI) に置き換えます。JSIは、JavaScriptがC++オブジェクトへの参照を保持し、その逆も可能にするインターフェースです。メモリ参照により、シリアライズのコストなしにメソッドを直接呼び出すことができます。

JSIは、React Nativeで人気のカメラライブラリであるVisionCameraがフレームをリアルタイムで処理することを可能にしています。一般的なフレームバッファは約30 MBで、これはフレームレートにもよりますが、毎秒約2 GBのデータ量に相当します。ブリッジのシリアライズコストと比較して、JSIはその量の連携データを容易に処理します。JSIは、データベース、画像、音声サンプルなど、他の複雑なインスタンスベースの型も公開できます。

新アーキテクチャでのJSIの採用により、すべてのネイティブ-JavaScript相互運用からこの種のシリアライズ作業がなくなります。これには、ViewTextのようなネイティブのコアコンポーネントの初期化や再レンダリングも含まれます。新アーキテクチャにおけるレンダリングパフォーマンスの調査と、測定された改善されたベンチマークについて詳しく読むことができます。

新アーキテクチャを有効にすることで何を期待できますか?

新アーキテクチャはこれらの機能や改善を可能にしますが、アプリやライブラリで新アーキテクチャを有効にしても、すぐにパフォーマンスやユーザー体験が向上するとは限りません。

例えば、同期的なレイアウトエフェクトや並行機能のような新しい機能を活用するには、コードのリファクタリングが必要になるかもしれません。JSIはJavaScriptとネイティブメモリ間のオーバーヘッドを最小限に抑えますが、データシリアライズがアプリのパフォーマンスのボトルネックでなかった可能性もあります。

アプリやライブラリで新アーキテクチャを有効にすることは、React Nativeの未来にオプトインすることです。

チームは、新アーキテクチャがもたらす新しい機能を積極的に研究・開発しています。例えば、Webとの整合性向上はMetaで活発に探求されている分野であり、React Nativeオープンソースエコシステムにも導入される予定です。

専用のdiscussions & proposalsリポジトリで進捗を追い、コントリビュートすることができます。

今日から新アーキテクチャを使うべきですか?

0.76では、新アーキテクチャはすべてのReact Nativeプロジェクトでデフォルトで有効になります。

うまく動作しないものを見つけた場合は、このテンプレートを使用してissueをオープンしてください。

何らかの理由で新アーキテクチャを使用できない場合は、オプトアウトすることも可能です。

Android

  1. android/gradle.propertiesファイルを開きます
  2. newArchEnabledフラグをtrueからfalseに切り替えます
gradle.properties
# 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

  1. ios/Podfileファイルを開きます
  2. PodfileのメインスコープにENV['RCT_NEW_ARCH_ENABLED'] = '0'を追加します(テンプレートのPodfile参照
diff
+ 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(
  1. 次のコマンドでCocoaPodsの依存関係をインストールします
shell
bundle exec pod install