2

I am new to react native, and i'm using new react-navigation v.5 in my react-native app. When i use createStackNavigator and createBottomTabNavigator together in NavigationContainer i have errors - "undefined is not an object" and "Another navigator is already registered for this container. You likely have multiple navigators under a single "NavigationContainer" or "Screen". Make sure each navigator is under a separate "Screen" container." (in React Navigation v.5). Please tell me where i`m wrong?

AppNavigation.tsx

import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import { MainScreen } from "../screens/MainScreen";
import { PostScreen } from "../screens/PostScreen";
import { AboutScreen } from "../screens/AboutScreen";
import { BookedScreen } from "../screens/BookedScreen";
import { CreateScreen } from "../screens/CreateScreen";
import { THEME } from "../theme";
import { Platform } from "react-native";
import { AppHeaderIcon } from "../components/AppHeaderIcon";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";

type RootStackParamList = {
  Main: undefined;
  Post: { postId?: string; date?: string; booked?: boolean };
  About: undefined;
  Booked: undefined;
  Create: undefined;
};

const headerButtons = (title: string, icon: string, callback: () => void) => {
  return (
    <HeaderButtons HeaderButtonComponent={AppHeaderIcon}>
      <Item title={title} iconName={icon} onPress={() => callback()} />
    </HeaderButtons>
  );
};

export const AppNavigation = () => {
  const Stack = createStackNavigator<RootStackParamList>();
  const Tab = createBottomTabNavigator();

  return (
    <NavigationContainer>
      <Stack.Navigator
        initialRouteName="Main"
        screenOptions={{
          headerTintColor:
            Platform.OS === "android" ? "white" : THEME.MAIN_COLOR,
          headerStyle: {
            backgroundColor:
              Platform.OS === "android" ? THEME.MAIN_COLOR : "white"
          }
        }}
      >
        <Stack.Screen
          name="Main"
          component={MainScreen}
          options={{
            headerTitle: "Мой блог",
            headerRight: () =>
              headerButtons("Сделать фото", "ios-camera", () =>
                console.log("Press camera")
              ),
            headerLeft: () =>
              headerButtons("drower", "ios-menu", () =>
                console.log("Press drower button")
              )
          }}
        />
        <Stack.Screen
          name="Post"
          component={PostScreen}
          options={({ route }) => ({
            headerTitle: `Пост от ${new Date(
              route.params.date
            ).toLocaleDateString()}`,
            headerRight: () =>
              headerButtons(
                "star",
                route.params.booked ? "ios-star" : "ios-star-outline",
                () => console.log("Press star button")
              )
          })}
        />
        <Stack.Screen name="About" component={AboutScreen} />
        <Stack.Screen name="Booked" component={BookedScreen} />
        <Stack.Screen name="Create" component={CreateScreen} />
      </Stack.Navigator>
      <Tab.Navigator>
        <Tab.Screen name="Post" component={PostScreen} />
        <Tab.Screen name="Booked" component={BookedScreen} />
      </Tab.Navigator>
    </NavigationContainer>
  );
};

App.tsx

import React, { useState } from "react";
import { AppLoading } from "expo";
import { bootstrap } from "./src/bootstrap";
import { AppNavigation } from "./src/navigation/AppNavigation";

export default function App() {
  const [isReady, setIsReady] = useState(false);

  if (!isReady) {
    //AppLoading - пока не завершится выполнение код дальше не пойдет
    return (
      <AppLoading
        startAsync={bootstrap}
        onFinish={() => setIsReady(true)}
        onError={err => console.log('AppLoading error - ', err)}
      />
    );
  }

  return <AppNavigation />;
}
lobs
  • 429
  • 5
  • 14

2 Answers2

0

This is the behavior that we want from the authentication flow: when users sign in, we want to throw away the state of the authentication flow and unmount all of the screens related to authentication, and when we press the hardware back button we expect to not be able to go back to the authentication flow. read this one - https://reactnavigation.org/docs/en/auth-flow.html#render-the-navigator-content

chief
  • 1
0

This paragraph helped me to understand: "You can think of this as there being separate navigation stacks within each tab, and that's exactly how we will model it in React Navigation". II fixed some inaccuracies in my code:

import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import { MainScreen } from "../screens/MainScreen";
import { PostScreen } from "../screens/PostScreen";
import { BookedScreen } from "../screens/BookedScreen";
import { THEME } from "../theme";
import { Platform } from "react-native";
import { AppHeaderIcon } from "../components/AppHeaderIcon";
import { HeaderButtons, Item } from "react-navigation-header-buttons";
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs";
import { Ionicons } from "@expo/vector-icons";

type RootStackParamList = {
  Main: undefined;
  Post: { postId?: string; date?: string; booked?: boolean };
  About: undefined;
  Booked: undefined;
  Create: undefined;
};

const Stack = createStackNavigator<RootStackParamList>();

const headerButtons = (title: string, icon: string, callback: () => void) => {
  return (
    <HeaderButtons HeaderButtonComponent={AppHeaderIcon}>
      <Item title={title} iconName={icon} onPress={() => callback()} />
    </HeaderButtons>
  );
};

function PostNavigator() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerTintColor: Platform.OS === "android" ? "white" : THEME.MAIN_COLOR,
        headerStyle: {
          backgroundColor:
            Platform.OS === "android" ? THEME.MAIN_COLOR : "white"
        }
      }}
    >
      <Stack.Screen
        name="Main"
        component={MainScreen}
        options={{
          headerTitle: "Мой блог",
          headerRight: () =>
            headerButtons("Сделать фото", "ios-camera", () =>
              console.log("Press camera")
            ),
          headerLeft: () =>
            headerButtons("drower", "ios-menu", () =>
              console.log("Press drower button")
            )
        }}
      />
      <Stack.Screen
        name="Post"
        component={PostScreen}
        options={({ route }) => ({
          headerTitle: `Пост от ${new Date(
            route.params.date
          ).toLocaleDateString()}`,
          headerRight: () =>
            headerButtons(
              "star",
              route.params.booked ? "ios-star" : "ios-star-outline",
              () => console.log("Press star button")
            )
        })}
      />
    </Stack.Navigator>
  );
}

function BookedNavigator() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerTintColor: Platform.OS === "android" ? "white" : THEME.MAIN_COLOR,
        headerStyle: {
          backgroundColor:
            Platform.OS === "android" ? THEME.MAIN_COLOR : "white"
        }
      }}
    >
      <Stack.Screen
        name="Booked"
        component={BookedScreen}
        options={{
          headerTitle: "Избранное",
          headerRight: () =>
            headerButtons("Сделать фото", "ios-camera", () =>
              console.log("Press camera")
            )
        }}
      />
      <Stack.Screen
        name="Post"
        component={PostScreen}
        options={({ route }) => ({
          headerTitle: `Пост от ${new Date(
            route.params.date
          ).toLocaleDateString()}`,
          headerRight: () =>
            headerButtons(
              "star",
              route.params.booked ? "ios-star" : "ios-star-outline",
              () => console.log("Press star button")
            )
        })}
      />
    </Stack.Navigator>
  );
}

export const AppNavigation = () => {
  const Tab = createBottomTabNavigator();

  return (
    <NavigationContainer>
      <Tab.Navigator
        screenOptions={({ route }) => ({
          tabBarIcon: ({ focused, color, size }) => {
            let iconName;
            if (route.name === "Post") {
              iconName = focused ? "ios-list-box" : "ios-list";
            } else if (route.name === "Booked") {
              iconName = focused ? "ios-star" : "ios-star-outline";
            }
            return <Ionicons name={iconName} size={size} color={color} />;
          }
        })}
        tabBarOptions={{
          activeTintColor: THEME.MAIN_COLOR,
          inactiveTintColor: "gray"
        }}
      >
        <Tab.Screen name="Post" component={PostNavigator} 
        options={{
          tabBarLabel: 'Все посты'
        }}/>
        <Tab.Screen name="Booked" component={BookedNavigator}
        options={{
          tabBarLabel: 'Избранное'
        }}
        />
      </Tab.Navigator>
    </NavigationContainer>
  );
};

lobs
  • 429
  • 5
  • 14