navigating from a tab screen to a stack screen - reactjs

For an IOS application, I have a stack that gets called in my tab navigator. I am trying to navigate from a bottom tab screen to a screen in the stack but I am getting the following error.
undefined is not an object (evaluating
'_this.props.navigation.navigate')
I would like to render the bottom tab across all screens. I am noticing this causes some interesting issues with goBack() as well in other places.
How can I navigate from the bottom tab screen to a stack screen?
Is the current implementation a bad practice?
I have provided this demo as well as the following code below. I think it is related to prop passing.
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Stack.Navigator
initialRouteName="Home">
<Stack.Screen name="Home" component= {Home} options={{headerShown: false}}/>
<Stack.Screen name="Screen1" component= {Screen1} options={{headerShown: false}}/>
</Stack.Navigator>
);
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarActiveTintColor: '#F60081',
tabBarInactiveTintColor: '#4d4d4d',
tabBarStyle: {
backgroundColor: '#d1cfcf',
borderTopColor: 'transparent',
},
}}
>
<Tab.Screen
name="Home"
component={MyTabs}
options={{
tabBarLabel: 'Home',
headerShown: false,
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
const Stack = createStackNavigator();
import * as React from 'react';
import {
Text,
View,
StyleSheet,
TouchableOpacity,
ImageBackground,
} from 'react-native';
const Images = [
{ id: '1', uri: require('./assets/snack-icon.png'), text: 'Test' },
{ id: '2', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
{ id: '3', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
{ id: '4', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
];
export default class Home extends React.Component {
thisNavigate = () => {
this.props.navigation();
};
renderList = (props) => {
return Images.map((item, i) => {
return (
<TouchableOpacity
onPress={() => this.props.thisNavigate.navigate('Screen2')}>
<ImageBackground
source={item.uri}
style={{
width: '100%',
height: 100,
shadowColor: '#000',
shadowOffset: { width: 1, height: 4 },
shadowOpacity: 1,
}}
imageStyle={{ borderRadius: 10 }}></ImageBackground>
</TouchableOpacity>
);
});
};
render() {
return <View style={{ flex: 1 }}>{this.renderList()}</View>;
}
}

In your example your Home component is:
export default class Home extends React.Component {
render() {
return <Screen1/> // here navigation is not passed because it is rendered not by navigator but manually
}
}
Just remove it, remove its Stack.Screen from MyTabs and set initialRouteName to Screen1, and it will work.
EDIT:
If you want to keep Home as it is, pass navigation like that:
export default class Home extends React.Component {
render() {
return <Screen1 navigation={this.props.navigation}/>
}
}
Or use useNavigation hook instead of props in Screen1 to get navigation.
EDIT2:
Working demo.

I think that you need to wrap your component withNavigation HOC
https://reactnavigation.org/docs/4.x/with-navigation/
That's because your component not directly from the component Navigator, so they don't have this.props.navigation, to make your component have navigation props in Class Component, you need to wrap your component using withNavigation HOC
example:
// Screen1.js
class Home extends React.Component {
renderList = (props) => {
return Images.map((item, i) => {
return (
<TouchableOpacity
onPress={() => this.props.navigation.navigate('Screen2')}>
<ImageBackground
source={item.uri}
style={{
width: '100%',
height: 100,
shadowColor: '#000',
shadowOffset: { width: 1, height: 4 },
shadowOpacity: 1,
}}
imageStyle={{ borderRadius: 10 }}></ImageBackground>
</TouchableOpacity>
);
});
};
render() {
return <View style={{ flex: 1 }}>{this.renderList()}</View>;
}
}
export default withNavigation(Home);
sorry, but I think withNavigation is removed from v5 / v6
but you can make one like this
import { useNavigation } from '#react-navigation/native';
// withNavigation.js
export default function withNavigation(WrappedComponent) {
return (props) => {
const navigation = useNavigation();
return <Component navigation={navigation} {...props} />
}
}

Related

React Native Stack Navigation not working

I am building a fitness app, and currently I am having a problem with React Native navigation stack, that I wasn't having before and not sure why this is happening.
As you can see in the video, after you select the type of training and number of days, it should go to the screen with the filtered workouts (WorkoutSelection).
Edit: I tried moving the navigation.navigate() outside the async function and it does navigate to the correct page, so I'm assuming it has something to do with it being inside a async function.
Here's the code for the confirm button:
async function confirmHandler() {
setIsLoading(true);
try {
const data = await getTrainingData(
selectedTrainingData.value,
selectedDaysData.value
);
setFilteredWorkouts(data);
setIsLoading(false);
navigation.navigate('WorkoutSelection');
} catch (error) {
console.log(error);
setIsLoading(false);
return (
<ErrorScreen message={'Something went wrong. Please try again.'} />
);
}
}
What is happening right now is whenever I click the confirm button, it goes to the home screen.
It does fetch the data from the API, I logged it to the console and it's working so, that shouldn't be the problem.
Here's the navigation code:
function TrainingOptionsStack() {
return (
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: GlobalStyles.colors.background },
headerTintColor: 'white',
headerTitleStyle: {
fontFamily: 'open-sans-semi-bold',
},
}}
>
<Stack.Screen
name='WorkoutsScreen'
component={WorkoutsScreen}
options={{ title: 'Workouts' }}
/>
<Stack.Screen
name='SelectPhase'
component={BlockOptions}
options={{ title: 'Training Phase' }}
/>
<Stack.Screen
name='WorkoutSelection'
component={WorkoutSelection}
options={{
title: 'Select a workout',
}}
/>
<Stack.Screen
name='SelectDay'
component={SelectDay}
options={{ title: 'Select Day' }}
/>
<Stack.Screen name='WorkoutOfTheDay' component={WorkoutOfTheDay} />
<Stack.Screen
name='PreviewModal'
component={PreviewModal}
options={{ presentation: 'modal', title: false }}
/>
</Stack.Navigator>
);
}
function AppNavigation() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={{
headerStyle: { backgroundColor: GlobalStyles.colors.background },
headerTintColor: 'white',
tabBarStyle: {
backgroundColor: GlobalStyles.colors.primary700,
paddingTop: 5,
height: 90,
},
headerTitleStyle: {
fontFamily: 'open-sans-semi-bold',
},
tabBarActiveTintColor: 'white',
}}
>
<Tab.Screen
name='Home'
component={HomeScreen}
options={{
title: 'Home',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='home-outline'
size={34}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
<Tab.Screen
name='Workouts'
component={TrainingOptionsStack}
options={{
title: 'Workouts',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='barbell-outline'
size={34}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
headerShown: false,
}}
/>
<Tab.Screen
name='RepMaxCalculator'
component={RepMaxCalculator}
options={{
title: 'Max Rep Calculator',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='calculator-outline'
size={30}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
<Tab.Screen
name='ProgressChart'
component={ProgressChart}
options={{
title: 'Progress Chart',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='bar-chart-outline'
size={30}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
And here's the 'WorkoutSelection' component -
import React, { useEffect } from 'react';
import { FlatList, StyleSheet, View } from 'react-native';
import SelectWorkout from '../components/SelectWorkout';
import FlatButton from '../components/UI/buttons/FlatButton';
import { GlobalStyles } from '../constants/styles';
import useAppContext from '../store/AppContext';
import { doc, setDoc, serverTimestamp } from 'firebase/firestore';
import { db } from '../firebase/firebaseConfig';
function WorkoutSelection({ navigation }) {
const {
filteredWorkouts,
previewWorkoutHandler,
setWorkoutPreviewTitle,
setCurrentWorkout,
currentWorkout,
userIsAuthenticated,
} = useAppContext();
// useEffect(() => {
// const docRef = doc(db, 'users', userIsAuthenticated.uid);
// async function addCurrentWorkoutToDataBase() {
// await setDoc(docRef, 'CurrentWorkout', currentWorkout, { merge: true });
// }
// addCurrentWorkoutToDataBase();
// }, [currentWorkout]);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<FlatButton
style={styles.headerButton}
onPress={() => navigation.replace('WorkoutsScreen')}
>
Cancel
</FlatButton>
),
});
}, []);
const id = filteredWorkouts
.flatMap((item) => item.workouts)
.flatMap((item) => item.id);
function previewHandler(item) {
previewWorkoutHandler(id[0]);
setWorkoutPreviewTitle(item.title);
navigation.navigate('PreviewModal');
}
function selectWorkoutHandler(item) {
setCurrentWorkout([item]);
navigation.navigate('WorkoutsScreen');
}
return (
<View style={styles.rootContainer}>
<FlatList
data={filteredWorkouts}
renderItem={({ item }) => (
<SelectWorkout
name={item.title}
onSelect={() => selectWorkoutHandler(item)}
onShowPreview={() => previewHandler(item)}
/>
)}
keyExtractor={(item) => item.id}
/>
</View>
);
}
export default WorkoutSelection;
const styles = StyleSheet.create({
rootContainer: {
backgroundColor: GlobalStyles.colors.background,
flex: 1,
},
headerButton: {
textDecorationLine: 'none',
marginRight: 24,
fontSize: 16,
fontFamily: 'open-sans-semi-bold',
},
});
Here's a little video to demonstrate the problem -
https://drive.google.com/file/d/1OoKlj2tZsL6sYqpcUnKUcQhAjmhTZGJZ/view?usp=share_link
What I expect is to go to the 'WorkoutSelection" screen so the user can complete the workout selection flow and I can update it in Firebase.
Appreciate any help!
Have you import all the component on above page?
e.g. if you want to use WorkoutSelection component then import that.
like this.
import {WorkoutSelection} from 'path here'.
and then use that component for the navigation.
Hope you got my point.
It is happening because you are using replace function instead of goBack or navigate in WorkoutSelection
So first change from replace to goBack or navigate
after selecting values WorkoutSelection , if you want to refresh WorkoutsScreen so your selected filter can be apply then you have two ways to do that
call api on focus change in WorkoutsScreen (follow below mentioned link )
https://reactnavigation.org/docs/function-after-focusing-screen/
fire event from WorkoutSelection before calling goBack or navigate and that event should be listened on WorkoutsScreen so when you get event , you can call api and refresh screen as per filter selection
I figured out the problem. I had to go back a few commits and start adding one thing at a time to try to get to the point where I was having the same problem.
The problem was in the App.js, the rootFunction that I created wasn't returning anything conditionally as I wanted. I had to restructure it a bit and got it working.
Totally missed it. Thanks all for the help!

cannot read property 'params' of undefined react native

I'm coding product detail but can't use the params property
Cannot read property 'params' of undefined
you can see my code here
import React from 'react';
import {Image, Text, View} from 'react-native';
const ProductDetail = ({route, navigation}) => {
const productID = route.params;
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Product Detail</Text>
</View>
);
};
export default ProductDetail;
Product Item
const CardHeight = 220;
const ProductItem = ({list}) => {
const navigation = useNavigation();
return (
<ScrollView>
{list?.map((item, index) => {
return (
<TouchableOpacity
style={{
borderRadius: sizes.radius,
overflow: 'hidden',
flexDirection: 'row',
}}
onPress={() => {
navigation.navigate('ProductDetails'), {productID: index};
}}>
.....
);
};
export default ProductItem;
MainNavigator
const Stack = createStackNavigator();
const MainNavigator = () => {
return (
<NavigationContainer>
<StatusBar hidden />
<Stack.Navigator>
<Stack.Screen
name="Root"
component={TabDrawer}
options={{
headerShown: false,
useNativeDriver: true,
gestureEnabled: false,
}}
/>
<Stack.Screen
name="ProductDetails"
component={productDetailScreen}
options={{headerShown: false}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default MainNavigator;
I tried to follow the Passing parameters to routes of react navigation without success
productDetail
I don't know where this error comes from can anyone explain to me specifically
Replace this navigation.navigate('ProductDetails'), {productID: index}; with below code:
navigation.navigate('ProductDetails',{productID: index})

React Navigation. How to navigate with class component?

I am trying to navigate with React Navigate.
When clicking on the image I get this error in console:
Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
I have tried different methods to navigate, but without success. Any suggestions?
This is my code:
import React, { Component } from 'react';
import { StyleSheet, Text, View, SafeAreaView, Image, TouchableHighlight } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { useNavigation } from '#react-navigation/native';
const Stack = createNativeStackNavigator();
import Home from './screens/homeScreen';
import Teams from './screens/teamsScreen';
export default class App extends Component {
GoTo({ screenName }) {
const navigation = useNavigation();
navigation.navigate(screenName);
}
render() {
return (
<SafeAreaView style={{flex: 1,}}>
<View style={styles.header}>
<TouchableHighlight onPress={() => this.GoTo('Teams')}>
<Image
style={styles.headerLogo}
source={{uri: 'https://yukigassen.no/uploads/Yukigassen_Logo_Big_Transparent.png'}}
resizeMode={'stretch'}
/>
</TouchableHighlight>
<View style={styles.headerRight}>
<Image
style={{width: 25, height: 25}}
source={{uri: 'https://icons.iconarchive.com/icons/dtafalonso/android-lollipop/512/Settings-icon.png'}}
/>
</View>
</View>
<View style={styles.screen}>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} options={{headerShown: false}} />
<Stack.Screen name="Teams" component={Teams} options={{headerShown: false}} />
</Stack.Navigator>
</NavigationContainer>
</View>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
header: {
height: '7.5%',
padding: 5,
},
headerLogo: {
width: 160,
height: 50,
marginLeft: 10,
},
headerRight:{
position: 'absolute',
right: 5,
top: 15,
},
screen: {
flex: 1,
},
});
useNavigation() is a hook that cannot be used with class components but there is a way around it. You can create a wrapper component and pass the navigation prop into your class component.
class App extends Component {
render() {
// Get it from props
const { navigation } = this.props;
...
}
}
// Wrap and export
export default function(props) {
const navigation = useNavigation();
return <App {...props} navigation={navigation} />;
}
Example from https://reactnavigation.org/docs/use-navigation/
you can try this approach, make that comp default route and check, lemme know if this helps :) thanks
import React, { Component } from 'react';
import { StyleSheet, Text, View, SafeAreaView, Image, TouchableHighlight } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import { useNavigation } from '#react-navigation/native';
const Stack = createNativeStackNavigator();
import Home from './screens/homeScreen';
import Teams from './screens/teamsScreen';
const DefaultRoute = () => {
GoTo({ screenName }) {
const navigation = useNavigation();
navigation.navigate(screenName);
}
return(
<SafeAreaView style={{flex: 1,}}>
<View style={styles.header}>
<TouchableHighlight onPress={() => this.GoTo('Teams')}>
<Image
style={styles.headerLogo}
source={{uri: 'https://yukigassen.no/uploads/Yukigassen_Logo_Big_Transparent.png'}}
resizeMode={'stretch'}
/>
</TouchableHighlight>
<View style={styles.headerRight}>
<Image
style={{width: 25, height: 25}}
source={{uri: 'https://icons.iconarchive.com/icons/dtafalonso/android-lollipop/512/Settings-icon.png'}}
/>
</View>
</View>
</SafeAreaView>
)
}
export default class App extends Component {
render() {
return (
<SafeAreaView style={styles.screen}>
<NavigationContainer>
<Stack.Navigator initialRouteName="DefaultRoute">
<Stack.Screen name="DefaultRoute" component={DefaultRoute} options={{headerShown: false}} />
<Stack.Screen name="Home" component={Home} options={{headerShown: false}} />
<Stack.Screen name="Teams" component={Teams} options={{headerShown: false}} />
</Stack.Navigator>
</NavigationContainer>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
header: {
height: '7.5%',
padding: 5,
},
headerLogo: {
width: 160,
height: 50,
marginLeft: 10,
},
headerRight:{
position: 'absolute',
right: 5,
top: 15,
},
screen: {
flex: 1,
},
});

Why my Bottom Tabs Navigator is re rendering unnecessary?

I am making an app in which there are three bottom tabs screen. HomeScreen, CartScreen and Profile Screen. I was wanting to check the re renders of my app using "Why did you re render" npm package and found a critical bug or is it so.
The MainTabScreen(The screen in which all the bottom tabs are nested) is re rendering as many times I change the screen, just because its props changed.
Here is the console log :
Here is the code of the MainTabScreen.js:
const MainTabScreen = () => {
return (
<Tab.Navigator
tabBarOptions={{
showLabel: false,
style: {
position: "absolute",
height: 40,
bottom: 0,
right: 0,
left: 0,
backgroundColor: "#fff",
},
}}
>
<Tab.Screen
name="MainHome"
component={MainHomeScreen}
options={{
tabBarIcon: ({ focused }) => (
<Icon.Home
// color={"#f62459"}
color={focused ? "#f62459" : "#302f2f"}
height={28}
width={28}
/>
),
}}
/>
<Tab.Screen
name="MainCart"
component={MainCartScreen}
options={{
tabBarIcon: ({ focused }) => (
<Icon.ShoppingCart
// color="#302f2f"
color={focused ? "#f62459" : "#302f2f"}
width={28}
height={28}
/>
),
}}
/>
<Tab.Screen
name="MainProfile"
component={MainProfileScreen}
options={{
tabBarIcon: ({ focused }) => (
<Icon.User
// color="#302f2f"
color={focused ? "#f62459" : "#302f2f"}
height={28}
width={28}
/>
),
}}
/>
</Tab.Navigator>
);
};
export default MainTabScreen;
Edit 11:27 05-09-21
The MainTabScreen renders inside AppStack screen which renders inside the AuthStack.js for authentication and this AuthStack.js renders inside the App.js
Here is the AppStack.js code:
const AppStack = () => {
return(
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="MainTabScreen" component={MainTabScreen} />
<Stack.Screen name="Product" component={Product} />
<Stack.Screen name="OrderScreen" component={OrderScreen} />
</Stack.Navigator>
)
This is the AuthStack.js
import React, {
useState,
useEffect,
useMemo,
useReducer,
useContext,
} from "react";
import { View, Text, Platform } from "react-native";
import AppStack from "./navigation/AppStack";
import RootStack from "./navigation/RootStack";
import { AuthContext } from "./context";
import * as SecureStore from "expo-secure-store";
import LottieView from "lottie-react-native";
import { useIsMounted } from "../screens/useIsMounted";
const AuthStack = () => {
const initialLoginState = {
isLoading: true,
accessToken: null,
};
// The rest of the authentication logic...
return (
<AuthContext.Provider value={authContext}>
{loginState.accessToken !== null ? <AppStack /> : <RootStack />}
</AuthContext.Provider>
);
};
export default AuthStack;
App.js
import React from "react";
import { StyleSheet, Text, View, Dimensions } from "react-native";
import { NavigationContainer } from "#react-navigation/native";
import store from "./redux/store";
const App = () => {
return (
<Provider store={store}>
<NavigationContainer>
<AuthStack />
</NavigationContainer>
</Provider>
);
};
export default App;
i found the solution...
use:
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
`<Tab.Navigator
tabBar={props => <MyTabBar {...props} />}>`
create separately TabBar (for example):
function MyTabBar({ state, descriptors, navigation, position }) {
const edge = useSafeAreaInsets();
const { colors } = useTheme();
return (
<View style={{ flexDirection: 'row', height: edge.bottom + 70, alignItems: 'center', justifyContent: 'space-between', backgroundColor: colors.cell }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
// The `merge: true` option makes sure that the params inside the tab screen are preserved
navigation.navigate({ name: route.name, merge: true });
}
};
if (label === 'Browse Radios') {
return <CenterTabBar isFocused={isFocused} onPress={() => { onPress(); }} />
} else {
return <MyTabBarButton isFocused={isFocused} onPress={() => { onPress(); }} label={label} />
}
})}
</View>
);
}
and create separately buttons for TabBar (for example):
const CenterTabBar = ({ onPress, isFocused }) => {
return (
<TouchableOpacity style={{
alignItems: 'center',
flex: 1
}}
onPress={() => {
onPress();
}}
>
{!isFocused && <Image style={styles.centerTabBar} source={require('../../assets/images/tab_browse.png')} />}
{isFocused && <Image style={styles.centerTabBar} source={require('../../assets/images/tab_browse_.png')} />}
</TouchableOpacity>
);
}
const MyTabBarButton = ({ onPress, isFocused, label }) => {
const { colors, dark } = useTheme();
return (
<TouchableOpacity style={{
alignItems: 'center',
flex: 1
}}
onPress={() => { onPress() }}
>
<Animated.Image style={[styles.icon_tab, { tintColor: isFocused ? '#00ACEC' : colors.tint }]} source={GetIconTab(label)} />
{isFocused && <Animated.View style={styles.circle} entering={BounceIn} />}
</TouchableOpacity>
);
}

React native createDrawerNavigator is not working

Could you please help me on this small issue.
I am very very new to React native and here i am training in learn react navigation. My problem is Drawer navigator is not working when i try to swap from left to right. But bottom navigator is working. Any idea whats wrong with this please.
Also: if you can please send me a good sample to learn this area.
I have some samples, but most of the samples are outdated.
MyCode:
import React from 'react';
import {TouchableOpacity, View, Text, Image} from 'react-native';
import {createAppContainer, createSwitchNavigator} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import {createBottomTabNavigator} from 'react-navigation-tabs';
import {createDrawerNavigator} from 'react-navigation-drawer';
import HomeScreen2 from '../screens/HomeScreen2';
import HomeScreen from '../screens/HomeScreen';
import Setup from '../screens/Setup';
const HomeStack = createStackNavigator(
{
//Defination of Navigaton from home screen
Home: { screen: HomeScreen },
// Details: { screen: ProfileScreen },
},
{
defaultNavigationOptions: {
//Header customization of the perticular Screen
headerStyle: {
backgroundColor: '#42f44b',
},
headerTintColor: '#FFFFFF',
title: 'Home',
//Header title
},
}
);
const SettingsStack = createStackNavigator(
{
//Defination of Navigaton from setting screen
Settings: { screen: Setup },
},
{
defaultNavigationOptions: {
//Header customization of the perticular Screen
headerStyle: {
backgroundColor: '#42f44b',
},
headerTintColor: '#FFFFFF',
title: 'Settings',
//Header title
},
}
);
const TabMain = createBottomTabNavigator(
{
TAB1: HomeStack,
TAB2: SettingsStack,
},
{
initialRouteName: 'TAB1',
}
)
const DrawHomeView = createStackNavigator({
HomeV: {screen: HomeScreen2}},
{
headerMode: 'float',
navigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: 'rgb(255,45,85)',
paddingLeft: 10,
paddingRight: 10
},
title: 'Map View',
headerTintColor: 'white',
headerLeft: <View>
<TouchableOpacity
onPress={() => {
if (navigation.state.isDrawerOpen === false) {
navigation.dispatch(DrawerActions.openDrawer());
} else {
navigation.dispatch(DrawerActions.closeDrawer());
}
}}>
<Text>Menu</Text>
</TouchableOpacity>
</View>
})
});
const DrawSettingsView = createStackNavigator({
SettingsV: {screen: Setup}},
{
headerMode: 'float',
navigationOptions: ({navigation}) => ({
headerStyle: {
backgroundColor: 'rgb(255,45,85)',
paddingLeft: 10,
paddingRight: 10
},
title: 'Map View',
headerTintColor: 'white',
headerLeft: <View>
<TouchableOpacity
onPress={() => {
if (navigation.state.isDrawerOpen === false) {
navigation.dispatch(DrawerActions.openDrawer());
} else {
navigation.dispatch(DrawerActions.closeDrawer());
}
}}>
<Text>Menu</Text>
</TouchableOpacity>
</View>
})
});
const TabMain2 = createDrawerNavigator(
{
TAB3: DrawHomeView,
TAB4: DrawSettingsView,
},
{
initialRouteName: 'TAB3',
}
)
export default createAppContainer(createSwitchNavigator(
{
Sub1: TabMain,
Sub2: TabMain2,
},
{
initialRouteName: 'Sub2',
}
));
Okay. After some dabbling with your code and the docs : https://reactnavigation.org/docs/nesting-navigators/
and please read the docs and try their examples as they are excellent descriptive and available for real time execution on snack.
For the second part of the question, i put together an example for you:
import * as React from 'react';
import { Button, View, Text } from 'react-native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
//Import necessary components
//Create Screens, Dummy screens in this case.
function NotificationsScreen({ navigation }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button onPress={() => navigation.goBack()} title="Go back home" />
</View>
);
}
function Feed ({ navigation, route }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{fontSize: 40}}>Feed </Text>
</View>
);}
function Home ({ navigation, route }) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text style={{fontSize: 40}}> Home </Text>
</View>
);}
// Here is your questions answer, just create a Bottom Navigator
// and a Drawer Navigator and nest them in each other after declaring your
// screens.
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
function HomeScreen({ navigation }) {
return (
<Tab.Navigator>
//Put your Tab screens here.
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Feed" component={Feed} />
</Tab.Navigator>
);
}
export default function App() {
return (
// For the main export create a navigation container and declare the
// Drawer Navigator inside the main navigation container, then use that to
// To Access your Tab navigator "HomeScreen" and put whatever else you
// Want in your Drawer Navigator.
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Notifications" component={NotificationsScreen} />
</Drawer.Navigator>
</NavigationContainer>
);
}
Explanation: You are only allowed to nest navigators within each other and not within the main navigation container.
This code has been tested using expo-cli 3.18.4

Resources