🔎

Expo × React Server Componentsでできること

に公開

Expo SDK 52の登場


Expo公式サイトより
ExpoはReact Nativeでの開発・デプロイを便利にするエコシステムツールです。

先月、Expoは大型アップデートをリリースしました。
色んな目玉機能が組み込まれたのですが、中でも注目されたのがServer Componentsです。
まだプロダクションでは利用できないものの、多くの開発者が期待を寄せています。

この記事では、それらの活用法、将来性について見ていきます。

Server-Driven UIってなに?

通常、UIはクライアントサイドで定義します。
しかし、従来の方法には

  • UIの微修正でも審査が必要
  • A/Bテスト、Feature Flagなどの実装が複雑になる

といった問題点がありました。

そこでServer-Driven UIという思想が生まれました。
Server-Driven UIはサーバーサイドでUIを定義しようという思想です。

Airbnbがこの設計思想を提唱し、NetflixやRedditなどもこの思想を取り入れています。

React ConfのYoutubeより


クライアントが

  1. サーバーからUIデータを取得
    (ページレイアウト、コンポーネントのスタイリング、テキストの値など)
  2. クライアントでUIをレンダリング

という流れでUIを構築します。

以下は、JSONレスポンスからAirbnbのUIが構築されるイメージです。


A Deep Dive into Airbnb’s Server-Driven UI Systemより

Server Componentsってなに?

Server ComponentsはReact18で登場した機能です。

Server Componentsはサーバーで実行されるコンポーネントです。

具体的には、

  1. レンダリングリクエストをサーバーに送る(クライアント)
  2. Reactツリーを構築する(サーバー)
  3. RSCペイロードに変換して、レスポンスを送る(サーバー)
  4. RSCペイロードからUIを構築する(クライアント)
    という手順でレンダリングされます。

RSCペイロードを詳しく見ていきましょう。

Introducing Universal React Server Components in Expo Routerより

3行目に着目すると、

4: I[123, ["/node_modules/react-native/index.js"], "Text"]

となっており、
サーバーからReact Nativeの<Text>を使いますよというデータが送信されています。

このような形で、

  • コンポーネント
  • その表示位置
  • その子コンポーネント

などをデータとして送信しているのです。

つまり、サーバーサイドでUIを定義することができます
実際、以下のようにしてServer Componentsを定義できます。

'use server';

import { Image, Text, View } from 'react-native';

export async function Pokemon() {
  const res = await fetch('https://pokeapi.co/api/v2/pokemon/2');
  const json = await res.json();
  return (
    <View style={{ padding: 8, borderWidth: 1 }}>
      <Text style={{ fontWeight: 'bold', fontSize: 24 }}>{json.name}</Text>
      <Image source={{ uri: json.sprites.front_default }} style={{ width: 100, height: 100 }} />

      {json.abilities.map(ability => (
        <Text key={ability.ability.name}>- {ability.ability.name}</Text>
      ))}
    </View>
  );
}

ExpoチームはReact NativeでもServer Componentsを利用して、完全なServer-Driven UIを実現しようとしているのです。

メリット

UIをリアルタイムで切り替えられる

UIの変更内容をデプロイすると、クライアントに瞬時に反映することができます。

使えそうな場面としては、

  • 緊急のUI修正をしたいとき
  • ユーザーごとに異なるレイアウトを表示したいとき
  • A/Bテストをしたいとき

などでしょうか。

AppleやGoogleの審査を受けずに即時反映できるので、大きなメリットになりそうです。

データフェッチにSuspenseが使える

Server Componentはasync functionとして定義できることから、Suspenseを使うことができます。
そのため、データ待機時のスケルトン表示などが簡単に実装できます。

<Suspense fallback={<SkeltonComponent />}>
    <SomeComponent />
</Suspense>

アプリサイズを削減できる

Server Componentsを使うことでクライアント側のサイズを抑えることができます。
ExpoではNext.jsのようにServer ComponentsとClient Componentsを共存させることができます。(experimentalですが)

Next.jsのDiscussionより

動きの必要な部分だけClient Componentsにすることで、クライント側のサイズを減らすことができます。

また、OTA(Over the Air)アップデートと比較しても、JavaScriptのサイズは小さく済むそうです。

OTAアップデートとの違い

React Nativeアプリは、JavaScriptからブリッジを経由して、ネイティブUIを描写します。


React Native公式サイトより

つまり、JavaScriptを差し替えればUIを変更できます
OTAアップデートは、この性質を利用したものです。

EASサーバーから最新のJavaScriptをダウンロードすることで、
審査なしでのUI更新を可能にしています。

OTAアップデートを1回もpublishしていない場合、アプリに埋め込まれたJavaScriptを読み込んで、UIをレンダリングします。

OTAアップデートをpublishするとどうなるでしょうか。
起動時に最新のJavaScriptがダウンロードされると、次の起動時からそちらを優先して読み込みます。ただし、アプリに埋め込まれたJavaScriptは削除されません。

OTAアップデートの仕組みによって

  • 埋め込みJavaScript
  • OTAアップデートのJavaScript

の両方が共存するため、ストレージ使用量が増えてしまいます
これはデメリットの1つです。


React NativeコントリビューターであるSzymon Rybczak氏は、OTAアップデートのデメリットを言及しています。


we need to replace entire JavaScript bundle, which can be quite large in size

非常に大きいサイズのアプリ全体のJavaScriptバンドルを置き換える必要があります。

This can be particularly challenging with a slow internet connection, as it can take a while to download.

これはダウンロードに時間がかかるため、特に低速のネット環境で弊害になります。

https://szymonrybczak.dev/blog/bringing-react-server-components-to-react-native

OTAアップデートはアプリ全体のJavaScriptバンドルをダウンロードします。
そのため、JavaScriptのサイズは課題になります。

一方、Server Componentsは必要なときに必要な部分だけサーバーからデータが送られるため、
より合理的と言えるかもしれません。

デメリット

サーバーが必要になる

Server-Driven UIという名前の通り、サーバーが必要になります。
モバイルとWebをExpoで開発するのであれば、サーバーが必須になるので、問題ないでしょう。

しかし、ExpoでWebアプリも開発することはあるでしょうか?

Webでは、Next.jsが圧倒的な人気を誇っており、エコシステムが整っています
Webでは、Cookieを用いたセッション管理やSSRを前提とするケースが多く、その点で Next.jsは非常に成熟したエコシステムを持っています。
そのため、WebはNext.js、モバイルはExpoというケースの方が多そうです。

個人的な予想なのですが、ExpoはOTAアップデート用のサーバーを提供しているので、
Server Components用のサーバーも提供するのではないかと予想しています。

これが実現すれば、Server-Driven UIの敷居がグッと下がりそうです。

サーバーへの負担が増える

Server-Driven UIを導入する場合、データ送信の他に、UIのレンダリングもサーバーで行います
従来の方法と比べてサーバーへの負担が増えるため、ランニングコストが上がる可能性があります

こちらの件も、Server Components用のサーバーも提供され、フリープランなどが用意されればある程度許容できるかもしれません。

ストアポリシーを考慮する必要がある

AppleやGoogleがServer-Driven UIに対して今後規制をかけるかもしれません
なぜなら、Server Componentsにネイティブビュー・イベントを組み込むことができるからです

Context Menuを組み込んだり、

Introducing Universal React Server Components in Expo Routerより

Apple Mapsなどを組み込んだりすることが可能です。

Introducing Universal React Server Components in Expo Routerより

プレゼン内ではこの仕組みについて解説していなかったので、考察してみます。

例えば、Apple MapsのネイティブビューはReact Nativeに搭載されていないため、
react-native-mapsというネイティブモジュールをクライアントに入れておく必要があります。

react-native-mapsをクライアントに入れた状態で、以下のようなServer Componentsのレンダリングリクエストが投げられるとします。

'use server';
import MapView from 'react-native-maps';

export const AppleMapsComponent = () => {
    return (
        <>
            <Text>Hogehoge</Text>
            <MapView
                initialRegion={{
                latitude: 37.78825,
                longitude: -122.4324,
                latitudeDelta: 0.0922,
                longitudeDelta: 0.0421,
              }}
            />
        </>
    )
}

サーバーは以下のような内容を含むRSCペイロードを返します。(予想)

9999: I[123, ["/node_modules/react-native-maps/src/index.ts"], "MapView"]

これを受け取ったクライアントは、ネイティブモジュールを呼び出してApple Mapsを描画することができるのだと思います。

そのため、

  • 何でもかんでもネイティブビュー・イベントを組み込めるわけではない
  • 事前に使いたいネイティブモジュールを入れる必要がある
  • OTAアップデートと同様、ネイティブモジュールを追加したい場合は審査に出す必要がある

というのが私の予想です。

とはいえ、サーバー側でネイティブビューやイベントも定義できるとなると、セキュリティ上の懸念が出てきます
もしかしたら、今後ストア側が新たな規制を作る可能性があります

まとめ

Server-Driven UI、モバイルでのServer Componentsの理解が難しく、記事を書くのにかなり時間がかかりました、、
冷静に考えると、Sever-Driven UIはWebでは当たり前だったことをモバイルにも適用しようとしているように見えます。
モバイルアプリはクライアントで色々やるのが当たり前」という常識を覆すようなアップデートで面白いなと思いました。
この記事で少しでも理解の助けになれたら幸いです。

参考

https://www.youtube.com/watch?v=djhEgxQf3Kw

https://www.youtube.com/watch?v=uLjqKLjE4UE

https://medium.com/airbnb-engineering/a-deep-dive-into-airbnbs-server-driven-ui-system-842244c5f5

https://szymonrybczak.dev/blog/bringing-react-server-components-to-react-native

https://www.wantedly.com/companies/rightcode/post_articles/921724

https://zenn.dev/yuu104/articles/react-server-component

Discussion