Using createStackNavigator along with createBottomTabNavigator in React Navigation version 5

I have recently started working with react native and I am using the latest version of react-navigation (v.5) in my react-native application. However, I encountered errors when trying to use createStackNavigator and createBottomTabNavigator together within NavigationContainer. The errors include "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." This issue occurred specifically in React Navigation v.5. Can someone please guide me on what I am doing 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: "My Blog",
            headerRight: () =>
              headerButtons("Take Photo", "ios-camera", () =>
                console.log("Press camera")
              ),
            headerLeft: () =>
              headerButtons("drawer", "ios-menu", () =>
                console.log("Press drawer button")
              )
          }}
        />
        <Stack.Screen
          name="Post"
          component={PostScreen}
          options={({ route }) => ({
            headerTitle: `Post from ${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 - until the code execution finishes, it will not proceed further
    return (
      <AppLoading
        startAsync={bootstrap}
        onFinish={() => setIsReady(true)}
        onError={err => console.log('AppLoading error - ', err)}
      />
    );
  }

  return <AppNavigation />;
}

Answer №1

We desire a specific behavior for the authentication flow: upon user sign-in, we aim to reset the authentication flow state and remove all screens related to authentication. Additionally, pressing the hardware back button should prevent users from navigating back to the authentication flow. For further information, please refer to - https://reactnavigation.org/docs/en/auth-flow.html#render-the-navigator-content

Answer №2

This paragraph helped me to comprehend: "The concept of having separate navigation stacks within each tab in React Navigation". I made necessary corrections 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: "My Blog",
          headerRight: () =>
            headerButtons("Take Photo", "ios-camera", () =>
              console.log("Press camera")
            ),
          headerLeft: () =>
            headerButtons("drawer", "ios-menu", () =>
              console.log("Press drawer button")
            )
        }}
      />
      <Stack.Screen
        name="Post"
        component={PostScreen}
        options={({ route }) => ({
          headerTitle: `Post from ${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: "Favorites",
          headerRight: () =>
            headerButtons("Take Photo", "ios-camera", () =>
              console.log("Press camera")
            )
        }}
      />
      <Stack.Screen
        name="Post"
        component={PostScreen}
        options={({ route }) => ({
          headerTitle: `Post from ${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: 'All Posts'
        }}/>
        <Tab.Screen name="Booked" component={BookedNavigator}
        options={{
          tabBarLabel: 'Favorites'
        }}
        />
      </Tab.Navigator>
    </NavigationContainer>
  );
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Unable to assign a value to the HTMLInputElement's property: The input field can only be set to a filename or an empty string programmatically

When attempting to upload an image, I encountered the error message listed in the question title: This is my template <input type="file" formControlName="avatar" accept=".jpg, .jpeg .svg" #fileInput (change)="uploa ...

Creating conditional keys using the Zod library based on the value of another key

Incorporating the TMDB API into my project, I am making an effort to enhance type safety by reinforcing some of the TypeScript concepts I am learning. To achieve this, I am utilizing Zod to define the structure of the data returned by the API. Upon invest ...

In Angular, is there a way to transform time into the format of YYYY-MM-DDThh:mm:ssTZD?

Our backend is built with Java and we are using the ISO 8601 standard for date formatting. In order to pass dates in this format, I require a method to convert the date into the specified format. In Java, we use: DateFormat iso8601 = new SimpleDateFormat( ...

Define variables using specific class components only

Consider a scenario where we define a class as follows: class House { street: string; pools: number; helicopterLandingPlace: boolean; } Now, I have created a service to update my house. putHouse(house: House) { // some put request } How ...

Identifying Data Types in Typescript Using a Union Type Guard

I came across this interesting type guard: enum X { A = "1" } const isNullableX = (value: any): value is X | null => false let bar: string | null = '' if (isNullableX(bar)) { console.log(bar) } Surprisingly, in the last con ...

The data type 'T[K]' does not meet the required conditions of 'string | number | symbol'

I am currently in the process of developing a straightforward function to eliminate duplicates from an array using TypeScript. While I acknowledge that there are numerous methods to accomplish this task, my main objective is to enhance my understanding of ...

Best practices for implementing the map function with TypeScript

I'm currently working on mapping types in a DB using the Map function in JavaScript. This is my first time trying to do this, and I'm eager to learn but I've hit a roadblock. Here is the structure of the DB: const db = { data: [ { ...

Exporting stylesheets in React allows developers to separate

I am trying to figure out how to create an external stylesheet using MaterialUI's 'makeStyles' and 'createStyles', similar to what can be done in React Native. I'm not sure where to start with this. export const useStyles = m ...

Rollup faces challenges when trying to bundle source code alongside Bazel and Typescript

I am attempting to create a bundle using rollup, typescript, and bazel environment. I am having trouble importing relative paths. Typescript builds correctly but rollup is unable to bundle the source. WORKSPACE # WORKSPACE workspace( name = "WORK ...

Identifying JavaScript Errors in a Web Page: A Guide to Cypress

Currently, I am working on creating a spec file that contains N it(s), with each it visiting a specific page within the application and returning the number of errors/warnings present. I have also shared my query here: https://github.com/cypress-io/cypres ...

Is there a way to specify a custom error type for returned data in rtk query?

Encountered a type error while using rtk query with the following content : Property 'data' does not exist on type 'FetchBaseQueryError | SerializedError'. Property 'data' does not exist on type 'SerializedError' ...

The attribute 'name' cannot be found within the class 'MyComponent'

I'm a beginner in Angular2 and I have no previous knowledge of version 1. Can you help me understand why this error is occurring and guide me on how to fix it? import { Component } from 'angular2/core'; @Component ({ selector: 'my- ...

Creating a TypeScript declaration file for a singular module

Consider the following directory structure: src/ ├── foo.ts ├── bar.ts ├── baz.ts ├── index.ts If foo.ts, bar.ts, and baz.ts each export a default class or object, for example in foo.ts: export default class Foo { x = 2; } I ...

What is the best way to define the height in a react project?

Greetings on this fine Friday. I've experimented with various options, but none of them appear to have any effect. "height": "100%" "height": "450px" "height": "auth" I would rather avoid a ...

Tips for displaying personalized data with MUI DatePicker

I need to create a React TypeScript component that displays a MUI DatePicker. When a new date is selected, I want a custom component (called <Badge>) to appear in the value field. Previously, I was able to achieve this with MUI Select: return ( ...

Using TypeScript to extract types from properties with specific types

My current challenge involves working with a filter object derived from an OpenAPI spec. The structure of this object is illustrated below: export interface Filters { field1: string[] field2: string[] field3: boolean field4: number } My goal is to ...

Learn how to restrict input to only specific characters in an input box using Angular2 and validations

Is it possible to restrict user input in an input box to only specific characters such as '7' and '8'? I tried adding validations with attributes like type="number", min="7" and max="8", but even then other keys can be inserted before v ...

What is the best way to convert an array of data into a dataset format in React Native?

Within my specific use case, I am seeking to reform the array structure prior to loading it into a line chart. In this context, the props received are as follows: const data = [26.727, 26.952, 12.132, 25.933, 12.151, 28.492, 12.134, 26.191] The objective ...

Difficulty with setting up Typescript in Visual Studio Code on MacOS Catalina

I'm currently facing an issue that appears to be related to the environment. How can I resolve it? And how do I link the installed TSC to my console? Steps to Recreate: npm install -g typescript was able to successfully install or update [email ...

Issue with Angular custom tag displaying and running a function

I have created a custom HTML tag. In my next.component.ts file, I defined the following: @Component({ selector: 'nextbutton', template: ` <button (click) = "nextfunc()">Next</button> ` }) export class NextComponent{ nextfunc( ...