📱

expo-routerを使った画面遷移を理解する

2024/12/10に公開

この記事はReact Native 全部俺 Advent Calendar 10目の記事です。

https://adventar.org/calendars/10741

このアドベントカレンダーについて

このアドベントカレンダーは @itome が全て書いています。

基本的にReact NativeおよびExpoの公式ドキュメントとソースコードを参照しながら書いていきます。誤植や編集依頼はXにお願いします。

expo-routerを使った画面遷移を理解する

https://docs.expo.dev/router/introduction/

React Nativeの画面遷移については、他のモバイルアプリ開発プラットフォームと大きく異なる特徴があります。AndroidやiOS、FlutterやWebアプリでは、プラットフォーム標準で画面遷移のためのAPIが用意されていますが、React Nativeには標準の画面遷移機能がありません。

代わりに、React Navigationというサードパーティライブラリがデファクトスタンダードとして使われてきました。多くのReact Nativeアプリケーションがこのライブラリを採用しています。

expo-routerとは

expo-routerは、React Navigationをベースとして、Expoプロジェクト向けに拡張された画面遷移ライブラリです。Expoプロジェクトを作成すると、デフォルトでexpo-routerが組み込まれています。

React Navigationをベースにしているため、細かい挙動のドキュメントを調べる際は「React Navigation ~~~」というキーワードで検索すると良いでしょう。

expo-routerの主な特徴

  • ファイルベースルーティング
    Next.jsなどのWebフレームワークで一般的な、ディレクトリ構造がそのままURLパスになるルーティングを実現できます。

  • ディープリンク対応
    アプリ内の各画面に対してURLを割り当てることができ、外部からのリンクで特定の画面に直接遷移できます。

  • TypeScriptサポート
    型安全な画面遷移を実装できます。ディレクトリ名を変更した際にコンパイルエラーが発生するため、リファクタリングが容易です。

  • クロスプラットフォーム対応
    ExpoのWebビルドにも追加設定なしで対応しています。

expo-routerの使い方

基本的なディレクトリ構造

まず、アプリのルートディレクトリにappディレクトリを作成し、その中に画面ごとのディレクトリを作成します。

app/
├── index.tsx      # ホーム画面
├── settings/      # 設定画面
│   └── index.tsx
└── profile/       # プロフィール画面
    └── index.tsx

各画面のコンポーネントは以下のように実装します:

// app/index.tsx
import { View, Text } from 'react-native';
import { Link } from 'expo-router';

export default function Home() {
  return (
    <View>
      <Text>Home Screen</Text>
      <Link href="/settings">Go to Settings</Link>
    </View>
  );
}

レイアウトの共通化

_layout.tsxを作成することで、複数の画面で共通のレイアウトを定義できます:

// app/_layout.tsx
import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack
      screenOptions={{
        headerStyle: {
          backgroundColor: '#f4511e',
        },
        headerTintColor: '#fff',
      }}
    />
  );
}

プラットフォーム固有の実装

ファイル名に.web.tsx.ios.tsxなどの拡張子を付けることで、プラットフォームごとに異なる実装を提供できます:

// app/settings/index.web.tsx
export default function WebSettings() {
  return (
    <div>
      <h1>Web Settings</h1>
    </div>
  );
}

// app/settings/index.native.tsx
export default function NativeSettings() {
  return (
    <View>
      <Text>Native Settings</Text>
    </View>
  );
}

動的ルーティング

ユーザーIDなどの動的なパラメータを含むルートは、ディレクトリ名を[パラメータ名]の形式で作成します:

// app/users/[userId]/index.tsx
import { useLocalSearchParams } from 'expo-router';

export default function UserProfile() {
  const { userId } = useLocalSearchParams();
  return (
    <View>
      <Text>User ID: {userId}</Text>
    </View>
  );
}

画面遷移の方法

画面遷移にはLinkコンポーネントを使用する方法と、useRouterフックを使用する方法があります:

// Linkコンポーネントを使用した宣言的な遷移
import { Link } from 'expo-router';

export default function Screen() {
  return (
    <Link href="/settings" asChild>
      <Pressable>
        <Text>Go to Settings</Text>
      </Pressable>
    </Link>
  );
}

// useRouterフックを使用したプログラムによる遷移
import { useRouter } from 'expo-router';

export default function Screen() {
  const router = useRouter();
  
  const handlePress = () => {
    router.push('/settings');
  };

  return (
    <Button title="Go to Settings" onPress={handlePress} />
  );
}

アニメーションについて

expo-routerのベースとなっているReact Navigationには、主に2つのナビゲーションスタイルがあります:

  1. @react-navigation/stack

    • JavaScriptでアニメーションを実装
    • アニメーションのカスタマイズが容易
    • パフォーマンスは若干劣る
  2. @react-navigation/native-stack

    • ネイティブのナビゲーションコンポーネントを使用
    • パフォーマンスが良好
    • アニメーションのカスタマイズに制限あり
    • OSによる挙動の差異が生じる可能性あり

Android

@react-navigation/stack @react-navigation/native-stack

iOS

@react-navigation/stack @react-navigation/native-stack

expo-routerはデフォルトでnative-stackを採用しています。プロジェクトの要件に応じて使い分けることをお勧めします。

FlutterやWebの開発者向けの注意点として、AndroidやiOSではアプリバー(ヘッダー)はアプリ全体で管理される要素です。expo-routerもこの考え方に従っているため、個々の画面でヘッダーの実装を書いていなくても表示されます。必要に応じてscreenOptions={{headerShown: false}}を設定することで非表示にできます。

// ヘッダーを非表示にする例
import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack
      screenOptions={{
        headerShown: false
      }}
    />
  );
}

これらの機能を使いこなすことで、React Nativeアプリにおいて柔軟かつ型安全な画面遷移を実装することができます。

ExpoでWebページのSSRはできるのか?

まだできません。現在Expoで作成したアプリのWeb版をServer Side Rendering(SSR)するためには、Next.jsなどの他のフレームワークを使う必要があります。

しかし全くサポートされていないわけではなく、2024/12現在、実験的な機能としてexpo-routerを使ったReact Server Component(RSC)を試すことができます。

https://docs.expo.dev/guides/server-components/

まだまだ安定していない機能なので本番環境で使うべきではないそうですが、これが実現すればNext.jsなどで作ったサイトと遜色ない機能とパフォーマンスのWebアプリをモバイルアプリと同じソースコードで使い分けられるかもしれません。

Flutterも同様にWebをターゲットとしてアプリを出力できますが、こちらは仕様上SSRがかなり難しいので、ExpoのRSCが実現すればマルチプラットフォームフレームワークとして本当に大きなアドバンテージになると思います。

今後が楽しみですね。

まとめ

今日はExpoの画面遷移についてまとめました。明日から1週間は実際にReact Nativeアプリを開発する上で知っておきたい概念をわかりやすく紹介していきます。明日はレイアウトの基本、FlexBoxについてです。

Discussion