我真的搞不明白 Expo Router

我真的搞不明白 Expo Router

之前因為沒時間好好研究 Expo Router,為了避免遇到bug所以一直使用react-navigation,這兩天剛好有時間嘗試expo-router差點沒被搞死(x

嵌套路由的返回

主要是卡在嵌套路由上,我試過這兩種路由結構:

第一種:

/app
├── settings.tsx
├── _layout.tsx
├── /basic
	├── length.tsx
  └── weight.tsx
└── home.tsx

第二種:

/app
├── settings.tsx
├── _layout.tsx
└── /home
    ├── /basic
        ├── length.tsx
        └── weight.tsx
    └── index.tsx

使用 Stack navigator,並且路由的跳轉都是使用 router.push,預期的行為是:

開啟APP 會在 home -> 進入 /basic/length -> 返回 /home -> 進入 /basic/weight -> 返回 /home -> 進入 /basic/temperature -> 返回 /home

但這兩種結構的實際行為都是這樣的:

開啟APP 會在 home -> 進入 /basic/length -> 返回 /home -> 進入 /basic/weight -> 返回 /basic/length -> 再返回才會到 /home -> 進入 /basic/temperature -> 返回 /basic/length > 再返回才會到 /home

總之它只會紀錄 /basic 內的頁面路由紀錄,直到 /basic 中沒有路由紀錄之後才會回到 /home。

解決辦法

最後的解決辦法是將 /basic 底下的內容全部扁平化,捨棄嵌套了。

/app
├── settings.tsx
├── _layout.tsx
└── /home
    ├── basic-length.tsx
    ├── basic-weight.tsx
    ├── _layout.tsx
    └── index.tsx

所以說這算解決了嗎?好像也不算😂

Header的設置

這是第二困擾我的部分…直接把我的例子搬上來說明好了。

app/_layout.tsx 使用的是 Drawer navigator

<Drawer
  screenOptions={drawerTheme}
  drawerContent={(props) => <CustomDrawerContent props={props} />}
>
  <Drawer.Screen
    name="home"
    options={{
      title: '首頁',
      drawerLabel: '首頁',
      headerShown: true,
      drawerIcon: ({ color, size }) => (
        <Text style={{ color, fontSize: size }}>🏠</Text>
      ),
    }}
  />
  <Drawer.Screen
    name="settings"
    options={{
      title: '設定',
      drawerLabel: '設定',
      headerShown: true,
      drawerIcon: ({ color, size }) => (
        <Text style={{ color, fontSize: size }}>⚙️</Text>
      ),
    }}
  />
</Drawer>

app/settings/_layout.tsx 使用的是 Stack navigator

import { Stack } from 'expo-router'

export default function SettingsLayout() {
  return (
    <Stack>
      <Stack.Screen
        name="index"
        options={{
          title: '設定',
          headerShown: false,
        }}
      />
      <Stack.Screen
        name="favorite-tools"
        options={{
          title: '常用工具設置',
          headerShown: false,
        }}
      />
    </Stack>
  )
} 

若將 settings 裡面的路由都設為 headerShown: false,那麼在 /settings/favorite-tools 頁面時 Header 顯示的也會是「設定」,並且沒有返回按鈕,只有 Drawer 的按鈕。

image 2

但我希望 /settings/index 就顯示「設定」, /settings/favorite-tools 就顯示「常用工具設置」,並且要有返回按鈕。

改為 headerShown: true

<Stack.Screen
  name="favorite-tools"
  options={{
    title: '常用工具設置',
    headerShown: true,
  }}
/>

又會出現雙重Header

image 3

最開始想到的做法是將 Drawer 的 header 隱藏,只顯示 Stack 的 Header,但這樣做的話會失去 Drawer 原本的側邊欄按鈕。

解決方法

如果想保持 Drawer 的側邊欄按鈕又要避免 Header 重複的話,我自己測試的結果是使用 Stack Header + 自定義drawer按鈕

/settings/_layout.tsx

import { Ionicons } from '@expo/vector-icons'
import { DrawerActions } from '@react-navigation/native'
import { Stack, useNavigation } from 'expo-router'
import { Button } from 'tamagui'

export default function SettingsLayout() {
  const navigation = useNavigation()
  return (
    <Stack>
      <Stack.Screen
        name="index"
        options={{
          title: '設定',
          headerShown: true,
          headerLeft: () => {
            return (
              <Button
                style={{ backgroundColor: 'transparent' }}
                p={10}
                onPress={() => {
                  navigation.dispatch(DrawerActions.openDrawer())
                }}
              >
                <Ionicons name="menu" size={26} color="black" />
              </Button>
            )
          },
        }}
      />
      <Stack.Screen
        name="favorite-tools"
        options={{
          title: '常用工具設置',
          headerShown: true,
        }}
      />
    </Stack>
  )
}

Drawer 就設為 headerShow: false

<Drawer
  screenOptions={drawerTheme}
  drawerContent={(props) => <CustomDrawerContent props={props} />}
>
  <Drawer.Screen
    name="home"
    options={{
      drawerLabel: '首頁',
      headerShown: true,
      drawerIcon: ({ color, size }) => (
        <Text style={{ color, fontSize: size }}>🏠</Text>
      ),
    }}
  />
  <Drawer.Screen
    name="settings"
    options={{
      drawerLabel: '設定',
      headerShown: true,
      drawerIcon: ({ color, size }) => (
        <Text style={{ color, fontSize: size }}>⚙️</Text>
      ),
    }}
  />
</Drawer>

沒用的 initialRouteName

/app
├── settings.tsx
├── _layout.tsx
└── /home
    ├── basic-length.tsx
    ├── basic-weight.tsx
    ├── _layout.tsx
    └── index.tsx

在 iOS 上設置 initialRouteName 沒有作用,會出現Unmatched Route,因為 iOS 會將 /index 作為首頁,不管有沒有 index 文件。

但在Android上即使不設置 initialRouteName ,一開啟APP也會自動將 /home/index 作為首頁:

<Drawer
  screenOptions={drawerTheme}
  initialRouteName="home"
  drawerContent={(props) => <CustomDrawerContent props={props} />}
>
  <Drawer.Screen
    name="home"
    options={{
      title: '首頁',
      headerShown: false,
      drawerLabel: '首頁',
      drawerIcon: ({ color, size }) => (
        <Text style={{ color, fontSize: size }}>🏠</Text>
      ),
    }}
  />
  <Drawer.Screen
    name="settings"
    options={{
      title: '設定',
      headerShown: false,
      drawerLabel: '設定',
      drawerIcon: ({ color, size }) => (
        <Text style={{ color, fontSize: size }}>⚙️</Text>
      ),
    }}
  />
</Drawer>

解決辦法

在 app 底下新增 index.tsx,強制 Redirect 到指定的首頁:

import { Redirect } from "expo-router"
const Index = () => {
  return <Redirect href="/home" />
}
export default Index
guest

0 評論
最舊
最新 最多投票
內聯回饋
查看全部評論