Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Expo Router は Expo 導入の決め手となるか フロントエンドカンファレンス沖縄2023 @Kaito-Dogi

Kaito-Dogi
November 18, 2023

Expo Router は Expo 導入の決め手となるか フロントエンドカンファレンス沖縄2023 @Kaito-Dogi

2023/11/18 開催のフロントエンドカンファレンス沖縄2023にて、『Expo Router は Expo 導入の決め手となるか』 というテーマで発表しました。

Kaito-Dogi

November 18, 2023
Tweet

More Decks by Kaito-Dogi

Other Decks in Programming

Transcript

  1. Expo Router は
    Expo 導⼊の決め⼿となるか
    フロントエンドカンファレンス沖縄2023
    @Kaito-Dogi

    View full-size slide

  2. 自己紹介
    @Kaito_Dogi
    @Kaito-Dogi
    ❏ 株式会社ゆめみ
    ❏ Android エンジニア
    ❏ React Native 挑戦中🔥
    ❏ 現地参戦2年⽬😎

    View full-size slide

  3. React Native で
    モバイル開発したことありますか?

    View full-size slide

  4. フロントエンドエンジニアとの開発で
    React Native を採⽤した

    View full-size slide

  5. 純粋な React Native
    vs
    Expo

    View full-size slide

  6. Expo とは
    ❏ React Native 開発のオープンソースプラットフォーム
    ❏ Expo SDK を使⽤してネイティブ機能に簡単にアクセスできる
    ❏ React Native の開発に追従して Expo が開発される
    ❏ Expo SDK v49.0.0 に対して React Native v0.72 が対応
    https://docs.expo.dev/versions/latest/

    View full-size slide

  7. Expo とは
    メリット デメリット
    ❏ 開発環境の構築が容易
    ❏ アプリのデプロイ‧配布が
    簡単になる
    ❏ React Native の更新に
    ⼀定の安定性が保証される
    ❏ Expo SDK に含まれない
    ネイティブ機能に
    アクセスできない
    ❏ アプリサイズが増加する
    ❏ 最新の React Native を
    利⽤できない

    View full-size slide

  8. Expo とは
    メリット デメリット
    ❏ 開発環境の構築が容易
    ❏ アプリのデプロイ‧配布が
    簡単になる
    ❏ React Native の更新に
    ⼀定の安定性が保証される
    ❏ Expo SDK に含まれない
    ネイティブ機能に
    アクセスできない
    ❏ アプリサイズが増加する
    ❏ 最新の React Native を
    利⽤できない

    View full-size slide

  9. Expo とは
    メリット デメリット
    ❏ 開発環境の構築が容易
    ❏ アプリのデプロイ‧配布が
    簡単になる
    ❏ React Native の更新に
    ⼀定の安定性が保証される
    ❏ Expo SDK に含まれない
    ネイティブ機能に
    アクセスできない
    ❏ アプリサイズが増加する
    ❏ 最新の React Native を
    利⽤できない

    View full-size slide

  10. Expo を導⼊してみた
    ❏ プロジェクトの適性
    ❏ Expo で制限されるネイティブ機能を使⽤しない(予定)
    ❏ ⼩‧中規模なプロジェクト
    ❏ スピード感を重視
    ❏ Android‧フロントエンドエンジニアの学習コスト
    ❏ 作り込みすぎず、顧客の需要を素早く掴めるか
    ❏ Expo Router をはじめとした、Expo コミュニティの活発さ
    https://expo.canny.io/

    View full-size slide

  11. Expo を導⼊してみた
    ❏ プロジェクトの適性
    ❏ Expo で制限されるネイティブ機能を使⽤しない(予定)
    ❏ 中規模なプロジェクト
    ❏ スピード感を重視
    ❏ Android‧フロントエンドエンジニアの学習コスト
    ❏ 作り込みすぎず、顧客の需要を素早く掴めるか
    ❏ Expo Router をはじめとした、Expo コミュニティの活発さ
    https://expo.canny.io/

    View full-size slide

  12. 本セッションで話すこと‧話さないこと
    ❏ 話すこと
    ❏ Expo Router の基本的な使い⽅
    ❏ React Navigation(後述)との共通点‧相違点
    ❏ Expo Router を導⼊して感じたメリット
    ❏ 話さないこと
    ❏ React Navigation から Expo Router への移⾏⽅法
    ❏ React Native や Expo そのものの込み⼊った話

    View full-size slide

  13. 本セッションで話すこと‧話さないこと
    ❏ 話すこと
    ❏ Expo Router の基本的な使い⽅
    ❏ React Navigation(後述)との共通点‧相違点
    ❏ Expo Router を導⼊して感じたメリット
    ❏ 話さないこと
    ❏ React Navigation から Expo Router への移⾏⽅法
    ❏ React Native や Expo そのものの込み⼊った話

    View full-size slide

  14. 本セッションで話すこと‧話さないこと
    ❏ 話すこと
    ❏ Expo Router の基本的な使い⽅
    ❏ React Navigation(後述)との共通点‧相違点
    ❏ Expo Router を導⼊して感じたメリット
    ❏ 話さないこと
    ❏ React Navigation から Expo Router への移⾏⽅法
    ❏ React Native や Expo そのものの込み⼊った話

    View full-size slide

  15. ❏ React Native の新しい画⾯遷移ライブラリ
    ❏ ファイルシステムベースルーティングが採⽤されている
    ❏ React Navigation 上に構築されている
    ❏ React Native の画⾯遷移ライブラリ
    ❏ Expo SDK v49 に対して Expo Router v2 を使⽤できる
    ❏ Expo SDK v50 に対して Expo Router v3 が予定されている
    Expo Router とは

    View full-size slide

  16. ❏ React Native の新しい画⾯遷移ライブラリ
    ❏ ファイルシステムベースルーティングが採⽤されている
    ❏ React Navigation 上に構築されている
    ❏ React Native の画⾯遷移ライブラリ
    ❏ Expo SDK v49 に対して Expo Router v2 を使⽤できる
    ❏ Expo SDK v50 に対して Expo Router v3 が予定されている
    Expo Router とは

    View full-size slide

  17. Expo Router の特徴
    ❏ スクリーンの描画
    ❏ そのディレクトリの _layout.tsx がまず描画される
    ❏ 次にそのディレクトリの index.tsx が描画される
    ❏ ディレクトリ構成をもとに、Stack、Tabs が⾃動的に構築される
    ❏ コンポーネント、router オブジェクト、useRouter で画⾯遷移
    ❏ “/hoge” は “app/hoge.tsx”、“app/events/hoge.tsx”
    ❏ どの画⾯も⾃動でディープリンク可能に(マッピング不要)
    https://docs.expo.dev/routing/introduction/

    View full-size slide

  18. チケット管理アプリを題材に🎟

    View full-size slide

  19. https://front-okinawa.connpass.com/
    Expo Router React Navigation

    View full-size slide

  20. Expo Router React Navigation

    View full-size slide

  21. チケット管理アプリ
    ❏ 2つのタブで構成
    ❏ スタックでイベント詳細へ
    ❏ モーダルで QR コードを表⽰

    View full-size slide

  22. チケット管理アプリ
    ❏ 2つのタブで構成
    ❏ スタックでイベント詳細へ
    ❏ モーダルで QR コードを表⽰

    View full-size slide

  23. チケット管理アプリ
    ❏ 2つのタブで構成
    ❏ スタックでイベント詳細へ
    ❏ モーダルで QR コードを表⽰

    View full-size slide

  24. チケット管理アプリ
    ❏ 2つのタブで構成
    ❏ スタックでイベント詳細へ
    ❏ モーダルで QR コードを表⽰

    View full-size slide

  25. ❏ パッケージのインストール
    ❏ 設定の変更
    ❏ エントリーポイントの変更
    ❏ app.json に scheme を追加
    ❏ babel.config.js の plugins に追加
    ❏ App.tsx を削除
    Expo Router のセットアップ
    https://docs.expo.dev/routing/installation/

    View full-size slide

  26. ❏ “expo-router” をインストール
    ❏ Expo Router セットアップ済みのテンプレートも
    パッケージのインストール
    % npx expo install expo-router
    Terminal
    % npx expo install expo-router
    Terminal

    View full-size slide

  27. エントリーポイントの変更
    - "main": "node_modules/expo/AppEntry.js",
    + "main": "expo-router/entry",
    package.json

    View full-size slide

  28. ❏ ディープリンクで使⽤される
    app.json に scheme を追加
    {
    ...,
    + "scheme": "expo-router-sample",
    }
    app.json

    View full-size slide

  29. module.exports = function (api) {
    api.cache(true);
    return {
    presets: ["babel-preset-expo"],
    plugins: [
    ...,
    + "expo-router/babel"
    ],
    };
    };
    babel.config.js の plugins に追加
    babel.config.js

    View full-size slide

  30. Layout routes
    ❏ そのディレクトリの _layout.tsx に記述する
    ❏ ページ共通で描画したいコンポーネントを描画できる
    ❏ Header、Footer、Context API など
    import { Slot } from "expo-router";
    export default function Layout() {
    return ;
    }
    app/_layout.tsx
    https://docs.expo.dev/routing/layouts/

    View full-size slide

  31. Layout routes
    import { Tabs } from "expo-router";
    import { TicketProvider } from "@/src/contexts/Ticket";
    export default function Layout() {
    return (

    ...

    );
    }
    app/_layout.tsx
    購⼊済みチケットを注⼊

    View full-size slide

  32. ❏ Navigator の外側に配置する
    import { TicketProvider } from "./src/contexts/Ticket";
    import { AppNavigator } from "./src/navigation";
    export default function App() {
    return (



    );
    }
    共通レイアウトの配置
    App.tsx
    購⼊済みチケットを注⼊

    View full-size slide

  33. ❏ 画⾯が重なっていくような画⾯遷移
    ❏ React Navigation の Native Stack Navigator をラップしている
    ❏ そのディレクトリの index.tsx がまず描画される
    Stack
    https://docs.expo.dev/router/advanced/stack/
    https://reactnavigation.org/docs/native-stack-navigator/
    import { Stack } from "expo-router";
    export default function Layout() {
    return ;
    }
    app/_layout.tsx

    View full-size slide

  34. Stack
    ❏ コンポーネントでスタック全体の設定
    ❏ コンポーネントで画⾯ごとの設定
    import { Stack } from "expo-router";
    export default function Layout() {
    return (


    ...

    );
    }
    app/events/_layout.tsx

    View full-size slide

  35. import { createNativeStackNavigator } from "@react-navigation/native-stack";
    export type EventStackParamList = {
    EventList: undefined;
    ...
    };
    const Stack = createNativeStackNavigator();
    export const EventStack: FC = () => {
    return (


    ...

    );
    };
    Native Stack Navigator
    navigation/EventStack.tsx

    View full-size slide

  36. import { createNativeStackNavigator } from "@react-navigation/native-stack";
    export type EventStackParamList = {
    EventList: undefined;
    ...
    };
    const Stack = createNativeStackNavigator();
    export const EventStack: FC = () => {
    return (


    ...

    );
    };
    Native Stack Navigator
    navigation/EventStack.tsx
    そのスタックの画⾯を
    型として定義

    View full-size slide

  37. import { createNativeStackNavigator } from "@react-navigation/native-stack";
    export type EventStackParamList = {
    EventList: undefined;
    ...
    };
    const Stack = createNativeStackNavigator();
    export const EventStack: FC = () => {
    return (


    ...

    );
    };
    Native Stack Navigator
    navigation/EventStack.tsx
    Navigator を初期化

    View full-size slide

  38. import { createNativeStackNavigator } from "@react-navigation/native-stack";
    export type EventStackParamList = {
    EventList: undefined;
    ...
    };
    const Stack = createNativeStackNavigator();
    export const EventStack: FC = () => {
    return (


    ...

    );
    };
    Native Stack Navigator
    navigation/EventStack.tsx
    各画⾯に対して
    Component を指定

    View full-size slide

  39. import { createNativeStackNavigator } from "@react-navigation/native-stack";
    export type EventStackParamList = {
    EventList: undefined;
    ...
    };
    const Stack = createNativeStackNavigator();
    export const EventStack: FC = () => {
    return (


    ...

    );
    };
    Native Stack Navigator
    navigation/EventStack.tsx
    最初に描画する
    画⾯を指定

    View full-size slide

  40. Dynamic routes
    ❏ [id].tsx で任意の id にマッチできる
    ❏ useLocalSearchParams でパラメータを取得できる
    https://docs.expo.dev/routing/create-pages/#dynamic-routes
    https://docs.expo.dev/router/reference/hooks/#uselocalsearchparams
    import { Redirect, useLocalSearchParams } from "expo-router";
    export default function Page() {
    const { id } = useLocalSearchParams();
    if (typeof id !== "string") return ;
    return ;
    }
    app/events/[id].tsx

    View full-size slide

  41. ❏ コンポーネントでは、Href オブジェクトで
    pathname、params を指定する
    import { Link } from 'expo-router';
    export default function Page() {
    ...
    href={{
    pathname: "/events/[id]",
    params: { id: "1" }
    }}>
    ...

    ...
    }
    Dynamic routes
    https://docs.expo.dev/routing/navigating-pages/#linking-to-dynamic-routes
    app/index.tsx

    View full-size slide

  42. ❏ route オブジェクトの params から取得する
    import { RouteProp } from "@react-navigation/native";
    type Props = {
    route: RouteProp;
    };
    export const EventDetailScreen: FC = ({ route }) => {
    const { id } = route.params;
    ...
    Passing parameters to routes
    https://reactnavigation.org/docs/params/
    components/screens/EventDetailScreen/EventDetailScreen.tsx

    View full-size slide

  43. ❏ パラメータの型を定義する
    ❏ パラメータが不要の場合は undefined とする
    import { createNativeStackNavigator } from "@react-navigation/native-stack";
    type EventStackParamList = {
    EventList: undefined;
    EventDetail: { id: string };
    };
    const Stack = createNativeStackNavigator();
    Passing parameters to routes
    https://reactnavigation.org/docs/typescript/#type-checking-screens
    navigation/EventStack.tsx

    View full-size slide

  44. Tabs
    ❏ iOS の Tab bars や Android の Navigation bar
    ❏ React Navigation の Bottom Tabs をラップしている
    ❏ そのディレクトリの index.tsx がまず描画される
    https://docs.expo.dev/router/advanced/tabs/
    https://reactnavigation.org/docs/bottom-tab-navigator/
    import { Tabs } from "expo-router";
    export default function Layout() {
    return ;
    }
    app/_layout.tsx

    View full-size slide

  45. Tabs
    ❏ コンポーネントでスタック全体の設定
    ❏ コンポーネントで画⾯ごとの設定
    import { Tabs } from "expo-router";
    export default function Layout() {
    return (



    );
    }
    app/_layout.tsx

    View full-size slide

  46. Tabs
    export default function Layout() {
    return (

    name="events"
    options={{
    title: "イベント",
    tabBarIcon: ({ color, focused }) => (

    ),
    }}
    />
    ...

    );
    }
    app/_layout.tsx

    View full-size slide

  47. Tabs
    export default function Layout() {
    return (

    name="events"
    options={{
    title: "イベント",
    tabBarIcon: ({ color, focused }) => (

    ),
    }}
    />
    ...

    );
    }
    app/_layout.tsx
    選択されたタブの
    アイコン‧タイトルの⾊

    View full-size slide

  48. Tabs
    export default function Layout() {
    return (

    name="events"
    options={{
    title: "イベント",
    tabBarIcon: ({ color, focused }) => (

    ),
    }}
    />
    ...

    );
    }
    app/_layout.tsx
    タブのタイトル

    View full-size slide

  49. Tabs
    export default function Layout() {
    return (

    name="events"
    options={{
    title: "イベント",
    tabBarIcon: ({ color, focused }) => (

    ),
    }}
    />
    ...

    );
    }
    app/_layout.tsx
    タブのアイコン

    View full-size slide

  50. Tabs
    ❏ href で遷移する画⾯を明⽰的に指定
    ❏ href を null にすると、タブとして表⽰されない
    import { Tabs } from "expo-router";
    export default function Layout() {
    return (




    );
    }
    app/_layout.tsx

    View full-size slide

  51. import { Tabs } from "expo-router";
    export default function Layout() {
    return (




    );
    }
    Tabs
    ❏ href で遷移する画⾯を選択
    ❏ href を null にすると、タブとして表⽰されない
    app/_layout.tsx

    View full-size slide

  52. ❏ href で遷移する画⾯を選択
    ❏ href を null にすると、タブとして表⽰されない
    import { Tabs } from "expo-router";
    export default function Layout() {
    return (




    );
    }
    Tabs
    app/_layout.tsx

    View full-size slide

  53. import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
    type RootTabParamList = {
    Event: undefined;
    ...
    };
    const Tab = createBottomTabNavigator();
    export const RootTab: FC = () => {
    return (


    ...

    );
    };
    Bottom Tabs
    navigation/RootTab.tsx

    View full-size slide

  54. import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
    type RootTabParamList = {
    Event: undefined;
    ...
    };
    const Tab = createBottomTabNavigator();
    export const RootTab: FC = () => {
    return (


    ...

    );
    };
    Bottom Tabs
    navigation/RootTab.tsx
    そのタブの画⾯を型として定義

    View full-size slide

  55. import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
    type RootTabParamList = {
    Event: undefined;
    ...
    };
    const Tab = createBottomTabNavigator();
    export const RootTab: FC = () => {
    return (


    ...

    );
    };
    Bottom Tabs
    navigation/RootTab.tsx
    Navigator を初期化

    View full-size slide

  56. import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
    type RootTabParamList = {
    Event: undefined;
    ...
    };
    const Tab = createBottomTabNavigator();
    export const RootTab: FC = () => {
    return (


    ...

    );
    };
    Bottom Tabs
    navigation/RootTab.tsx
    各画⾯に対して
    Component を指定

    View full-size slide

  57. import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
    type RootTabParamList = {
    Event: undefined;
    ...
    };
    const Tab = createBottomTabNavigator();
    export const RootTab: FC = () => {
    return (


    ...

    );
    };
    Bottom Tabs
    navigation/RootTab.tsx
    最初に描画する
    画⾯を指定

    View full-size slide

  58. Issue #763 initialRouteName が動かない
    https://github.com/expo/router/issues/763

    View full-size slide

  59. Issue #763 initialRouteName が動かない
    https://github.com/expo/router/issues/763

    View full-size slide

  60. Issue #763 initialRouteName が動かない
    https://github.com/expo/router/issues/763

    View full-size slide

  61. Issue #763 initialRouteName が動かない
    ❏ 最初に表⽰される index.tsx でリダイレクト
    https://docs.expo.dev/router/reference/redirects/
    import { Redirect } from "expo-router";
    export default function Page() {
    return ;
    }
    app/index.tsx

    View full-size slide

  62. Modals
    ❏ の presentation で “modal” を指定する
    https://docs.expo.dev/router/advanced/modals/
    import { Stack } from "expo-router";
    export default function Layout() {
    return (

    ...


    );
    }
    app/_layout.tsx

    View full-size slide

  63. Issue #640 Android で Modals が動かない
    https://github.com/expo/router/issues/640

    View full-size slide

  64. Issue #640 Android で Modals が動かない
    ❏ JS Stack Navigator で実装する
    ❏ Material Design として適切ではないためという意⾒もあり、
    Android で Modals を使うかは議論が必要
    https://docs.expo.dev/router/advanced/stack/#javascript-stack-with-react-navigationstack
    https://developer.apple.com/design/human-interface-guidelines/modality

    View full-size slide

  65. ❏ JsStack を初期化
    import { ParamListBase, StackNavigationState } from "@react-navigation/native";
    import {
    createStackNavigator,
    StackNavigationEventMap,
    StackNavigationOptions,
    TransitionPresets,
    } from "@react-navigation/stack";
    import { withLayoutContext } from "expo-router";
    const { Navigator } = createStackNavigator();
    const JsStack = withLayoutContext<
    StackNavigationOptions,
    typeof Navigator,
    StackNavigationState,
    StackNavigationEventMap
    >(Navigator);
    Issue #640 Android で Modals が動かない
    app/tickets/_layout.tsx

    View full-size slide

  66. ❏ の presentation で “modal” を指定
    Issue #640 Android で Modals が動かない
    export default function Layout() {
    return (

    name="[id]"
    options={{
    ...TransitionPresets.ModalPresentationIOS,
    presentation: "modal",
    }}
    />

    );
    }
    app/tickets/_layout.tsx

    View full-size slide

  67. ❏ の presentation で “modal” を指定
    Issue #640 Android で Modals が動かない
    export default function Layout() {
    return (

    name="[id]"
    options={{
    ...TransitionPresets.ModalPresentationIOS,
    presentation: "modal",
    }}
    />

    );
    }
    app/tickets/_layout.tsx

    View full-size slide

  68. Typed routes
    ❏ 画⾯の絶対パスをユニオン型として⽣成してくれる
    // prettier-ignore
    type StaticRoutes = `/` | `/_layout` | `/events/_layout` | `/events/` |
    `/tickets/_layout` | `/tickets/`;
    // prettier-ignore
    type DynamicRoutes = `/events/${SingleRoutePart}` |
    `/${CatchAllRoutePart}` | `/tickets/${SingleRoutePart}`;
    // prettier-ignore
    type DynamicRouteTemplate = `/events/[id]` | `/[...unmatched]` |
    `/tickets/[id]`;
    .expo/types/router.d.ts

    View full-size slide

  69. ❏ Expo Router v2(Expo SDK v49)では experimental
    ❏ app.json で Typed routes を有効化
    {
    "expo": {
    ...
    + "experiments": {
    + "typedRoutes": true
    + }
    }
    }
    app.json
    Typed routes
    https://docs.expo.dev/router/reference/typed-routes/

    View full-size slide

  70. ❏ ParamList を定義する
    ❏ その Navigator 内でネストされた Navigator、画⾯の名前を
    型付けするのみ(グローバルに遷移できるわけではない)
    export type EventStackParamList = {
    EventList: undefined;
    EventDetail: { id: string };
    };
    Type checking with TypeScript
    navigation/EventStack.tsx
    https://reactnavigation.org/docs/typescript/

    View full-size slide

  71. Unmatched routes
    ❏ [...unmatched].tsx でカスタマイズできる
    ❏ レスト構⽂(...)を使⽤していれば名前は⾃由
    ❏ “/404” で遷移できる
    ❏ Expo Router v3 から Not found routes が出る予定
    ❏ +not-found.tsx に記述する
    ❏ ネストされたレベルから全てのルートにマッチ
    https://docs.expo.dev/routing/error-handling/#unmatched-routes
    https://docs.expo.dev/router/reference/not-found/

    View full-size slide

  72. Unmatched routes
    ❏ [...unmatched].tsx でカスタマイズできる
    ❏ レスト構⽂(...)を使⽤していれば名前は⾃由
    ❏ “/404” で遷移できる
    ❏ Expo Router v3 から Not found routes が出る予定
    ❏ +not-found.tsx に記述する
    ❏ ネストされたレベルから全てのルートにマッチ
    https://docs.expo.dev/routing/error-handling/#unmatched-routes
    https://docs.expo.dev/router/reference/not-found/

    View full-size slide

  73. Top-level src directory
    ❏ src ディレクトリを app に含められる
    ❏ src/app はルートの app よりも優先される
    ❏ config ファイル、public ディレクトリはルートに置く
    ❏ 開発中に移動した場合、キャッシュをクリア
    https://docs.expo.dev/router/reference/src-directory/
    % npx expo start --clear
    Terminal

    View full-size slide

  74. その他の機能👀

    View full-size slide

  75. ❏ グループ構⽂ “()”
    ❏ URL にセグメントが表⽰されない
    ❏ app/auth/home.tsx は “app/auth/home” にマッチ
    ❏ app/(auth)/home.tsx は “app/home” にマッチ
    ❏ useSegments でグループ名の⽂字列を取得できる
    Groups
    https://docs.expo.dev/routing/layouts/#groups
    https://docs.expo.dev/router/reference/hooks/#usesegments

    View full-size slide

  76. Deep linking
    ❏ どの画⾯も⾃動でディープリンク可能に
    ❏ マッピング不要
    ❏ その他の実装は従来通り必要(割愛)
    https://docs.expo.dev/guides/deep-linking/

    View full-size slide

  77. Deep linking
    ❏ 画⾯とディープリンクをマッピングする必要がある
    https://reactnavigation.org/docs/deep-linking/
    import { LinkingOptions, NavigationContainer } from "@react-navigation/native";
    const linking: LinkingOptions = {
    prefixes: [ ... ],
    config: {
    screens: { ... },
    },
    };
    export const AppNavigator: FC = () => {
    return (
    ...
    );
    };
    navigation/AppNavigator.tsx

    View full-size slide

  78. API routes
    https://blog.expo.dev/rfc-api-routes-cce5a3b9f25d
    https://github.com/expo/expo/pull/24429
    import { ExpoRequest, ExpoResponse } from 'expo-router/server';
    export async function POST(req: ExpoRequest): Promise {
    ...
    return ExpoResponse.json(json);
    }
    app/+api.tsx
    ❏ API(サーバーサイドロジック)をプロジェクト内で実装できる
    ❏ +api.ts の接尾辞のファイルで作成
    ❏ HTTP メソッドが⼀致したときに関数が実⾏される
    ❏ Expo Router v3 で beta 版リリース予定

    View full-size slide

  79. Expo Router を使⽤して感じたメリット
    ❏ ファイルシステムベースルーティングの恩恵
    ❏ 型定義やオブジェクトの初期化の必要がなく、簡潔に書ける
    ❏ Typed routes がないと扱いづらい
    ❏ Hooks API が便利
    ❏ React Navigation では Screen ⽤の Component に ScreenProps
    (navigation や route オブジェクト)を渡す必要がある
    ❏ React Navigation との互換性があり、乗り換えやすい

    View full-size slide

  80. まとめ
    ❏ Expo 導⼊の背景に Expo Router をはじめとした Expo
    コミュニティの活発さがあった
    ❏ ファイルシステムベースルーティングを採⽤しており、
    簡潔に書けるようになった
    ❏ Expo Router は React Navigation をラップしており、React
    Navigation との互換性がある

    View full-size slide

  81. 参考記事
    ❏ Expo Documentation
    https://docs.expo.dev/
    ❏ React Navigation
    https://reactnavigation.org/
    ❏ Expo Feedback
    https://expo.canny.io/
    ❏ expo/router
    https://github.com/expo/router
    ❏ Human Interface Guidelines | Apple Developer Documentation
    https://developer.apple.com/design/human-interface-guidelines
    ❏ Material Design
    https://m3.material.io/
    ❏ Evenline - Event Booking App UI Kit
    https://ui8.net/unpixel/products/evenline---event-booking-app-ui-kit

    View full-size slide

  82. 12/09(⼟)に渋⾕で LT &交流会を開催します🎉
    https://coopello2.connpass.com/event/301314/

    View full-size slide

  83. ありがとうございました🙌

    View full-size slide