expo / router

[ARCHIVE]: Expo Router has moved to expo/expo -- The File-based router for universal React Native apps
https://docs.expo.dev/routing/introduction/
1.36k stars 113 forks source link

initial route #428

Closed cyberman-io closed 1 year ago

cyberman-io commented 1 year ago

Summary

i expect the initial route will be /chat/index but the initial route always /index in the route directory File system:

Minimal reproducible example

export default function Layout(){

return (
 <Drawer initialRouteName="chat">
        <Drawer.Screen
          name="chat"
          options={{
            drawerLabel: "Chat",
            title: "Chat"}}
        />
        <Drawer.Screen
          name="translate"
          options={{
            drawerLabel: "Translate",
            title: "Translate"}}
        />
        <Drawer.Screen
          name="history"
          options={{
            drawerLabel: "History",
            title: "History"}}
        />
      </Drawer>
)
}

export const unstable_settings = {

  initialRouteName: "chat",
};
EvanBacon commented 1 year ago

initial route name indicates which file (out of a stack) should always be rendered. If the app opens without a deep link, then the route will be / which matches /index and not /chat. You can setup a redirect to move pages automatically if you want.

hannojg commented 1 year ago

You can setup a redirect to move pages automatically if you want.

May I ask how?

lucacri commented 1 year ago

I just wasted 6 hours today trying to do the same thing you wanted/thought you could do with the "initialRouteName" thing.

Eventually i found a way to do the redirect:

import { Redirect } from "expo-router";

const Index = () => {
    return <Redirect href="/discussions" />;
};
export default Index;
Savinvadim1312 commented 1 year ago

I just wasted 6 hours today trying to do the same thing you wanted/thought you could do with the "initialRouteName" thing.

Eventually i found a way to do the redirect:

import { Redirect } from "expo-router";

const Index = () => {
  return <Redirect href="/discussions" />;
};
export default Index;

Thanks! @EvanBacon consider adding this to the documentation. It's a very common use-case to have some nested (tabs, drawer) screen as the default home screen.

The redirect is not that straightforward. I spent a good amount of time trying to fix this with the initialRouteName and couldn't figure it out

rudgal commented 1 year ago

Just used expo-router v2 for the first time and also just spent hours getting the initial route to work.

So I think this is definitely worth adding to documentation, since it is a pretty common use case as @Savinvadim1312 pointed out. Also knowing react-navigation, one would expect the property initialRouteName to have an effect.

Utilizing the redirect seems to be a valid workaround, but in my case leads to the problem described in #740 .

aadilmallick commented 1 year ago

For issue #740 , the fix is to wait for the navigation state to be ready. This is a preliminary fix for now, but I'm hoping the documentation will get better in the coming years:

import { View, Text } from "react-native";
import React from "react";
import { Redirect } from "expo-router";
import { useNavigation, useRootNavigation } from "expo-router";
import { LoadingScreen } from "../components/StyleComponents";

// index.tsx, referring to route /

export default function index() {
  const navigation = useRootNavigation();
  const [ready, setReady] = React.useState(false);

  React.useEffect(() => {
    if (!navigation?.isReady) return;

    setReady(true);
  }, [navigation?.isReady]);

  if (ready) return <Redirect href="/whatever-route" />;

  return <LoadingScreen />;
}
ccctask commented 1 year ago

it's work for me both some nested screens

this is my file tree

/app/_layout.jsx

import { Slot } from "expo-router";
export default function RootLayout() {
return <Slot />;
}

/app/drawer/_layout.jsx init router is /app/drawer/stack/index.jsx, its also worker when i change initialRouteName to "pageOne" the default page will be /app/drawer/pageOne.jsx


import { Drawer } from "expo-router/drawer";

export default function DrawerLayout() { return ( <Drawer screenOptions={{ drawerType: "slide", overlayColor: "transparent", drawerStyle: { flex: 1, width: "65%", padding: 20, backgroundColor: "transparent", }, sceneContainerStyle: { backgroundColor: "transparent" }, }} initialRouteName="stack"

); }


/app/drawer/stack/_layout.jsx
```javascript
import { Stack } from "expo-router";

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


> other pages  is default template 
```javascripts
import { View, Text } from "react-native";
import React from "react";

const pageOne = () => {
    return (
        <View>
            <Text>pageOne</Text>
        </View>
    );
};

export default pageOne;

this is my packages version "@react-navigation/drawer": "^6.6.3", "@react-navigation/native": "^6.0.2", "expo": "~49.0.8", "expo-font": "~11.4.0", "expo-linking": "~5.0.2", "expo-router": "2.0.0",

tonven commented 9 months ago

@lucacri @Savinvadim1312 maybe an unrelated question. But when having this redirect in initial component, for milliseconds I can see this initial screen of index.tsx. How would you implement it, so a user will not notice this redirect?

bnussman commented 9 months ago

Same issue here @tonven.

Everyone keeps suggesting redirecting, but it causes a super ugly flash of the initial screen. Is there any real way to handle initial routes without a redirect? The Expo Router docs don't provide a solution to this or even mention anything about it. Is this just an artifact in local dev?

https://github.com/expo/router/assets/6440455/bb0863de-4c58-4db8-a4d3-54d00dd42e0d

@KornelKwiatkowski Why did you laugh at this? Say it to my face please

omerkaz commented 9 months ago

@lucacri @Savinvadim1312 maybe an unrelated question. But when having this redirect in initial component, for milliseconds I can see this initial screen of index.tsx. How would you implement it, so a user will not notice this redirect?

this is also a sad part.

MoSattler commented 9 months ago

@lucacri @Savinvadim1312 maybe an unrelated question. But when having this redirect in initial component, for milliseconds I can see this initial screen of index.tsx. How would you implement it, so a user will not notice this redirect?

Similar problem here. Before initial render I would like to determine if I need to show a login page or not. Right now I have to always go to the login page, check there if user is logged in, and then reroute. Which is not super nice, and has this flicker going on.

johnhaup commented 9 months ago

@MoSattler I agree that there should be a more straightforward way to define the initial route using the router. But I was still able to achieve what I needed using the splash screen module. Just show your splash or some placeholder loading screen until you determine where you'd like to send the user.

https://docs.expo.dev/router/appearance/#splash-screen

matthewhausman commented 8 months ago

initial route name indicates which file (out of a stack) should always be rendered. If the app opens without a deep link, then the route will be / which matches /index and not /chat. You can setup a redirect to move pages automatically if you want.

@EvanBacon

What if there is no index.tsx?

bnussman commented 8 months ago

Turns out I needed to use Static Options to reduce flickering. Dyanmic Options inherently will cause flickering.

chanphiromsok commented 8 months ago

if Redirect without flick the mobile screen it would good to take it in project

filippo-brigati commented 6 months ago

Hi everyone, I'm having a problem with expo router that I can't resolve, this is my file structure

My goal would be to redirect the user to the page (tabs)/index by default or after logging in, and this works correcty in development with expo-go app, but when i build the app for play store i can't be redirected to index page by default, in fact, after logging in, the splash screen appears and the redirect doesn't happen...

I tried to do a different redirect after login (activity/feelings page) and it works correctly, the problem is that i cannot reach the index page.

I'm using SDK 50 and expo router 3.4.8, this in my sign-in page (only redirect method):

    const signInWithPassword = async () => {
      setIsLoading(true);

      const { data, error } = await supabase.auth.signInWithPassword({
        email: username,
        password: password,
      });

      if (error) {
        Alert.alert(error.message);
        setIsLoading(false);
      } else {
        await signIn(data.session.access_token, data.session.refresh_token);
        setIsLoading(false);
        console.log("After login, navigating to home page");
        router.replace("/");
      }
    };

and this is my app/_layout.tsx

import type { Theme } from "@react-navigation/native";
import { DarkTheme, ThemeProvider } from "@react-navigation/native";
import { Stack } from "expo-router";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { AuthProvider } from "../context/AuthContext";
import { GestureHandlerRootView } from "react-native-gesture-handler";
import * as SplashScreen from "expo-splash-screen";
import { useColorScheme } from "react-native";

export const unstable_settings = {
  initialRouteName: "(tabs)",
};

// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();

export default function Root() {
  const colorScheme = useColorScheme();

  const DefaultTheme: Theme = {
    dark: false,
    colors: {
      primary: "rgb(0, 122, 255)",
      background: "rgb(255, 255, 255)",
      card: "rgb(255, 255, 255)",
      text: "rgb(28, 28, 30)",
      border: "rgb(216, 216, 216)",
      notification: "rgb(255, 59, 48)",
    },
  };

  SplashScreen.hideAsync();

  return (
    <ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
      <AuthProvider>
        <GestureHandlerRootView style={{ flex: 1 }}>
          <SafeAreaProvider>
            <Stack>
              <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
              <Stack.Screen name="sign-in" options={{ headerShown: false }} />
              <Stack.Screen name="sign-up" options={{ headerShown: false }} />
              <Stack.Screen
                name="activity/note"
                options={{ headerShown: false }}
              />
              <Stack.Screen
                name="activity/feelings"
                options={{ headerShown: false }}
              />
              <Stack.Screen name="modal" options={{ presentation: "modal" }} />
            </Stack>
          </SafeAreaProvider>
        </GestureHandlerRootView>
      </AuthProvider>
    </ThemeProvider>
  );
}

and finally this is my app/(tabs)/layout.tsx

import { useAuth } from "../../context/AuthContext";
import { useColorScheme, Text } from "react-native";

import { SimpleLineIcons } from "@expo/vector-icons";
import { AntDesign } from "@expo/vector-icons";

export default function TabLayout() {
  const { isLoading, userToken } = useAuth();
  const colorScheme = useColorScheme();
  // You can keep the splash screen open, or render a loading screen like we do here.
  if (isLoading) {
    return <Text>Loading...</Text>;
  }

  // need to be able to access the (auth) group and sign in again.
  if (!userToken) {
    // On web, static rendering will stop here as the user is not authenticated
    // in the headless Node process that the pages are rendered in.
    return <Redirect href="/sign-in" />;
  }

  return (
    <Tabs
      screenOptions={{
        tabBarActiveTintColor: "#2f95dc",
        headerShown: false,
        tabBarShowLabel: false,
      }}
    >
      <Tabs.Screen
        name="index"
        options={{
          title: "Home",
          tabBarIcon(props) {
            return <AntDesign name="home" size={24} color={colorScheme === "light" ? (props.focused ? "black" : "gray") : (props.focused ? "white" : "#ced4da")} />
          }
        }}
      />
      <Tabs.Screen
        name="metrics"
        options={{
          title: "Metrics",
          tabBarIcon(props) {
            return <SimpleLineIcons name="graph" size={24} color={colorScheme === "light" ? (props.focused ? "black" : "gray") : (props.focused ? "white" : "#ced4da")} />
          }
        }}
      />
      <Tabs.Screen
        name="expert"
        options={{
          title: "Expert",
          tabBarIcon(props) {
            return <AntDesign name="staro" size={24} color={colorScheme === "light" ? (props.focused ? "black" : "gray") : (props.focused ? "white" : "#ced4da")} />
          }
        }}
      />
      <Tabs.Screen
        name="settings"
        options={{
          title: "Impostazioni",
          tabBarIcon(props) {
            return <AntDesign name="setting" size={24} color={colorScheme === "light" ? (props.focused ? "black" : "gray") : (props.focused ? "white" : "#ced4da")} />
          },
        }}
      />
    </Tabs>
  );
}

@EvanBacon I'm probably doing something wrong, but I can't figure out where.

Thanks everyone for the help!

ArthurDias01 commented 6 months ago

I spent literally more than 2 days in this, and still can't figure it out, seems like this is very inconsistent as expo-router v3


import { queryClient } from "@/src/lib/query";
import { useReactQueryDevTools } from "@dev-plugins/react-query";
import FontAwesome from "@expo/vector-icons/FontAwesome";
import { QueryClientProvider } from "@tanstack/react-query";
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import { useEffect } from "react";
import "react-native-gesture-handler";
import { ThemeProvider } from "styled-components";

import {
  Inter_400Regular,
  Inter_500Medium,
  Inter_700Bold,
} from "@expo-google-fonts/inter";

import {
  Nunito_400Regular,
  Nunito_600SemiBold,
  Nunito_700Bold,
} from "@expo-google-fonts/nunito";

import theme from "@/global/styles/theme";
import { AuthProvider } from "@/src/providers/auth";
import { ConfigNotificationContextProvider } from "@/src/providers/config-notifications-ctx";
import { UpdateProvider } from "@/src/providers/update-ctx";
import { DrawerToggleButton } from "@react-navigation/drawer";
import Drawer from "expo-router/drawer";
import { GestureHandlerRootView } from "react-native-gesture-handler";

export {
  // Catch any errors thrown by the Layout component.
  ErrorBoundary
} from "expo-router";

export const unstable_settings = {
  // Ensure that reloading on `/modal` keeps a back button present.
  initialRouteName: "(tabs)",
};

// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();

export default function RootLayout() {
  useReactQueryDevTools(queryClient);
  const [loaded, error] = useFonts({
    SpaceMono: require("@assets/fonts/SpaceMono-Regular.ttf"),
    Inter_400Regular,
    Inter_500Medium,
    Inter_700Bold,
    Nunito_400Regular,
    Nunito_600SemiBold,
    Nunito_700Bold,
    ...FontAwesome.font,
  });

  // Expo Router uses Error Boundaries to catch errors in the navigation tree.
  useEffect(() => {
    if (error) throw error;
  }, [error]);

  // Hide the splash screen once the app is ready. (fonts loaded)
  useEffect(() => {
    if (loaded) {
      SplashScreen.hideAsync();
    }
  }, [loaded]);

  if (!loaded) {
    return null;
  }

  return <RootLayoutNav />;
}

function RootLayoutNav() {
  return (
    <ThemeProvider theme={theme}>
      <QueryClientProvider client={queryClient}>
        <UpdateProvider>
          <AuthProvider>
            <ConfigNotificationContextProvider>
              <GestureHandlerRootView style={{ flex: 1 }}>
                <Drawer
                  screenOptions={{
                    headerShown: false,
                    drawerActiveTintColor: theme.colors.primary,
                    drawerInactiveTintColor: theme.colors.text,
                    swipeEdgeWidth: 80,
                  }}
                  initialRouteName="/"
                >
                  <Drawer.Screen
                    name="index"
                    options={{
                      drawerLabel: "Tabs",
                      headerLeft: () => <DrawerToggleButton />,
                      drawerItemStyle: { display: "none" },
                    }}
                  />
                  <Drawer.Screen
                    name="(tabs)"
                    options={{
                      drawerLabel: "Tabs",
                      headerLeft: () => <DrawerToggleButton />,
                    }}
                  />
                  <Drawer.Screen
                    name="minha-conta"
                    options={{
                      drawerLabel: "Minha Conta",
                      headerLeft: () => <DrawerToggleButton />,
                    }}
                  />
                  <Drawer.Screen
                    name="anunciar"
                    options={{
                      drawerLabel: "anunciar",
                      headerLeft: () => <DrawerToggleButton />,
                    }}
                  />
                  <Drawer.Screen
                    name="+not-found"
                    options={{
                      drawerLabel: "anunciar",
                      headerLeft: () => <DrawerToggleButton />,
                      drawerItemStyle: { display: "none" },
                    }}
                  />
                </Drawer>
              </GestureHandlerRootView>
            </ConfigNotificationContextProvider>
          </AuthProvider>
        </UpdateProvider>
      </QueryClientProvider>
    </ThemeProvider>
  );
}

even creating the index with redirect will not redirect to the correct route.

image

https://github.com/expo/router/assets/83284629/6332b5cf-29b5-47b9-8c34-95dde7d3226c

marklawlor commented 6 months ago

@ArthurDias01 This repo is for v2 and is only maintained for security purposes. If you are having an issue, please create a new issue at https://github.com/expo/expo

Also, reporting an issue with a code snippet of a single file does not met the criteria for a minimal reproduce-able repo. I can see from your video that the app is crashing from an error - but you have not provided me enough information to advise on what that error is. The best method is simply to share your repo so I can run the project.

ArthurDias01 commented 6 months ago

I got it to work after some heavy debugging, clearing all cache, reinstalling all dependencies and creating a new clean build. @marklawlor thanks. Will try to be more specific to the issue next time.

a-eid commented 2 months ago

I just wasted 6 hours today trying to do the same thing you wanted/thought you could do with the "initialRouteName" thing.

Eventually i found a way to do the redirect:

import { Redirect } from "expo-router";

const Index = () => {
  return <Redirect href="/discussions" />;
};
export default Index;

the only issue I see with this is the transition / animation.

benomzn commented 1 month ago

I just wasted 6 hours today trying to do the same thing you wanted/thought you could do with the "initialRouteName" thing. Eventually i found a way to do the redirect:

import { Redirect } from "expo-router";

const Index = () => {
    return <Redirect href="/discussions" />;
};
export default Index;

the only issue I see with this is the transition / animation.

Yeah, the user can see a white screen before redirect

Diego-Fdez commented 1 month ago

Acabo de perder 6 horas hoy tratando de hacer lo mismo que querías / pensabas que podías hacer con lo de "initialRouteName".

Finalmente encontré una manera de hacer la redirección:

import { Redirect } from "expo-router";

const Index = () => {
  return <Redirect href="/discussions" />;
};
export default Index;

This worked for me...

fredrikburmester commented 1 month ago

Is there no fix to this?

KovalevAnton commented 1 month ago

It seems like I’m not the only one having this problem. I’m using < Slot /> with initialRouteName, but it doesn’t seem to have any effect. It always shows '/' instead of '/home'.

Samox commented 3 weeks ago

Same. I'm creating a stack navigator

And when I navigate to /onboarding/questions I'd like to be on /onboarding/questions/0 by default to have an id = 0. For now I navigate to /onboarding/questions, the component [id].tsx is properly read but with id being undefined, I default it to 0 manually for now but I'm not confortable with that