Tab and Stack Navigator on same screen - react-native

When I navigate to a Tab via push both the Tab Navigator and Stack Navigator are displayed; however, when I navigate to a Stack Navigator via Tab Navigator only the Stack Navigator is displayed. How do I display both the Tab Navigator and Stack Navigator when I push to a Stack Navigator Screen? My app module:
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
function TabNavigator() {
return (
<Tab.Navigator>
<Tab.Screen name="User" component={UserDetailScreen} />
<Stack.Screen name="Feed" component={FeedScreen} />
</Tab.Navigator>
)
}
function MyStack() {
return (
<Stack.Navigator initialRouteName='Logout'>
<Stack.Screen name="Logout" component={LoginScreen} />
<Stack.Screen name="User" component={TabNavigator} />
<Stack.Screen name="UserForm" component={UserFormScreen} />
<Stack.Screen name="ItemForm" component={ItemFormScreen} />
<Stack.Screen name="Swap" component={SwapDetailScreen} />
</Stack.Navigator>
);
}
export default function App() {
console.log("test");
console.log(UserDetailScreen);
return (
<NavigationContainer>
<MyStack />
</NavigationContainer>
);
}

The TabBar is only visible when you're inside the TabNav. In your case that's either the UserDetailScreen or the FeedScreen. In order to display both, TabNav and StackNav, you need to change your nesting accordingly.
StackNav
- LoginScreen // no TabBar displayed
- TabNav
- UserDetailScreen
- FeedScreen
- UserFormScreen // no TabBar displayed
- ItemFormScreen // no TabBar displayed
- SwapDetailScreen // no TabBar displayed
Solution A: put StackNavs into your Tabs (recommended):
- TabNav
-StackNav // e.g. all user related screens
- LoginScreen
- UserDetailScreen
- UserFormScreen
-StackNav // e.g. all feed related screens
- FeedScreen
- ItemFormScreen
- SwapDetailScreen
Hint: if you want to have your login without Tabs you could exclude it from any navigator:
- LoginScreen
- TabNav
-StackNav // e.g. all user related screens
- UserDetailScreen
- UserFormScreen
-StackNav // e.g. all feed related screens
- FeedScreen
- ItemFormScreen
- SwapDetailScreen
In your code you can do sth. like this (Pseudocode):
<!-- language: lang-js -->
...
render(){
if (this.state.userIsLoggedIn){
return <TabNav />;
}
else {
return <View> My Login Screen </View>
}
}
...
Solution B: use one single Stack and customize your TabNav (might be interesting for some use cases):
- TabNav
- StackNav
- LoginScreen
- UserDetailScreen
- UserFormScreen
- FeedScreen
- ItemFormScreen
- SwapDetailScreen
In your code you can do sth. like this (Pseudocode):
...
<Tabs.Navigator tabBar = { props => <CustomTabBar {...props} /> }>
<Tabs.Screen name="TabBar" component={ getScreenStack } />
</Tabs.Navigator>
...
const CustomTabBar = ({ navigation }) => {
return (
<View>
<Button
title="User"
onPress={() => { navigation.navigate('UserScreen') }}
/>
)
}
...
function getScreenStack(){
return (
<Stack.Navigator>
<Stack.Screen name="Login” component=”LoginScreen” />
...
</Stack.Navigator>
)
}
...
If performance is not an issue I would stick to solution A, because solution B has several drawbacks:
Tab Indicators need to be tracked manually
Back button behaviour is odd and needs to be adjusted, because Tabs are handled as part of Stack
Hope that helps.

Why your FeedScreen use <Stack.Screen/> instead of Tab.Screen/>?
<Tab.Navigator>
<Tab.Screen name="User" component={UserDetailScreen} />
<Stack.Screen name="Feed" component={FeedScreen} />
</Tab.Navigator>
I am confused about this, it may be the problem.

Related

React Native Navigation: Another Navigator is already registered for this container

I want my component to render a TopTab Navigator on the top and also a Drawer Navigator at the same time.
So something like
<TopTab.Navigator>
<TopTab.Screen />
</TopTab.Navigator>
<Drawer.Navigator>
<Drawer.Screen />
</Drawer.Navigator>
However I'm getting an error of "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 "ScreenContainer"
Why dont you try using it like this, drawerNavigator holds as the main wrapper and inside it topTab
const HomeScreen = () => {
return(
<TopTab.Navigator>
<TopTab.Screen />
</TopTab.Navigator>
)
}
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
</Drawer.Navigator>
</NavigationContainer>
)
}
This should work, feel free for doubts
You have to set Tab navigator inside drawer navigator, you can search to get better solutions like "how we use multiple navigator in react native?"
visit below link
https://dev.to/easybuoy/combining-stack-tab-drawer-navigations-in-react-native-with-react-navigation-5-da

How to navigate from Home screen to Login screen in a nested navigation ? React Navigation v6

So I'm trying to improve my navigation in my React Native project using React Navigation. I would like to know how to navigate through a nested navigator from Home to Login screen.
navigation.ts
export type RootStackParamList = {
AuthorizedTabStack: BottomTabScreenProps<AuthorizedTabNavigationList>;
AuthorizedModalStack: NavigatorScreenParams<AuthorizedModalList>;
UnauthorizedStack: NavigatorScreenParams<UnauthorizedStackList>;
};
export type AuthorizedTabNavigationList = {
Home: undefined;
Planner: undefined;
};
export type AuthorizedModalList = {
InputModal: undefined;
};
export type UnauthorizedStackList = {
Login: undefined;
};
In my MainNavigator.tsx, I've implemented this...
<NavigationContainer>
<Stack.Navigator screenOptions={{ headerShown: false }}>
{auth.currentUser ? (
<>
<Stack.Screen name="AuthorizedTabStack" component={TabNavigation} />
<Stack.Screen
name="AuthorizedModalStack"
component={ModalNavigation}
/>
</>
) : (
<Stack.Screen
name="UnauthorizedStack"
component={UnauthorizedStack}
/>
)}
</Stack.Navigator>
</NavigationContainer>
The UnauthorizedStackList is basically a StackNavigator
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Login" component={LoginScreen} />
</Stack.Navigator>
I tried using this and there is an error: The action 'NAVIGATE' with payload {"name":"UnauthorizedStack","params":{"screen":"Login"}} was not handled by any navigator.
Do you have a screen named 'UnauthorizedStack'?
const navigation = useNavigation<NavigationProps>();
const handlePressLogOut = () => {
logOut()
navigation.navigate('UnauthorizedStack', { screen: 'Login' });
};
Please let me know if there are better practices on nested navigator or anything else in the code as well. I would like to learn more!
Your navigation.navigate(...) is called before auth state updates, because changing state is not happening right away. You cannot navigate to a screen, that is not rendered.
A few notes about your code:
you don't have to call navigation.navigate(...) after logging out, because you are conditionally rendering screens with auth.currentUser, so UnauthorizedStack will be rendered right after logOut(),
if Login screen is the only screen in your stack, probably there is no need for using stack,

Dismiss the nested stack navigator

In my react-native project I am using libraries
"#react-navigation/native": "^5.8.10",
"#react-navigation/stack": "^5.12.8",
I have nested navigator, like this:
// root level I have a stack navigator where it contains two screens, `Home` and `Settings`.
const App = ()=> {
const rootStack = createStackNavigator();
return (
<NavigationContainer>
<rootStack.Navigator>
<rootStack.Screen name="Home" component={Home} />
<rootStack.Screen name="Settings" component={Settings} />
</rootStack.Navigator>
</NavigationContainer>
);
}
// The Settings screen is a nested stack navigator
const Settings = ()=> {
const settingsStack = createStackNavigator();
return (
<settingsStack.Navigator>
<settingsStack.Screen name="SettingsOne" component={SettingsOneScreen} />
<settingsStack.Screen name="SettingsTwo" component={SettingsTwoScreen} />
</settingsStack.Navigator>
);
}
As you can see, the Settings screen is actually another level (nested) stack navigator.
On SettingsOneScreen, there is a button navigates user to SettingsTwoScreen.
const SettingsOneScreen = ({navigation}) => {
...
return (
...
<Button onPress={()=>navigation.navigate("SettingsTwo")}/>
)
}
Now, on SettingsTwoScreen, I have a button, I would like to close the whole settings navigator stack when user tap on the button. That's dismiss the whole settings stack & show user the Home. How to achieve it?
const SettingsTwoScreen = ({navigation}) => {
...
return (
...
<Button onPress={/*dismiss the settings stack*/}/>
)
}
(Of course I can't use the navigation.goBack() which only navigate user back to the previous screen i.e. SettingOneScreen in this case.)
1-) use navigate.
//this will go back to Home and remove any screens after that.
navigation.navigate('Home')
docs say that.
In a stack navigator, calling navigate with a screen name will result in different behavior based on if the screen is already present or not. If the screen is already present in the stack's history, it'll go back to that screen and remove any screens after that. If the screen is not present, it'll push a new screen.
2-) use reset.
navigation.reset({
index: 0,
routes: [{ name: 'Home' }],
})}
see docs about reset
3-) use replace then goBack.
//from SettingsOne call replace instead navigate
//this will remove SettingsOne and push SettingsTwo
navigation.replace('SettingsTwo');
//then from SettingsTwo
//calling goBack will back to home because SettingsOne removed in last step
navigation.goBack();
4-) use one stack with pop.
import { StackActions } from '#react-navigation/native';
//merge two stacks in one
<NavigationContainer>
<rootStack.Navigator>
<rootStack.Screen name="Home" component={Home} />
<rootStack.Screen name="SettingsOne" component={SettingsOneScreen} />
<rootStack.Screen name="SettingsTwo" component={SettingsTwoScreen} />
</rootStack.Navigator>
</NavigationContainer>
//back 2 screen
navigation.dispatch(StackActions.pop(2));
see docs about pop
for methods 1, 2 you can try snack here.
I have faced the same problem before, this will help you more
<Button onPress={()=>navigation.navigate("Settings",{
screen: 'SettingsTwo',
params: { data: data }//put here the data that you want to send to SettingTow
)}
/>
//more explanation
goto = (data) => {
navigation.navigate('parent_stack', {
screen: 'screen_on_children_stack',
params: { data: data }
});
}
You can create a Switch navigator for the "root" app and create two stacks "home" and "setting".
const Root = ()=> (createAppContainer(createSwitchNavigator(
{
Home: Home,
Settings: Settings,
},
{
initialRouteName: 'Home',
}
)
// The Settings screen is a nested stack navigator
const Settings = ()=> {
const settingsStack = createStackNavigator();
return (
<settingsStack.Navigator>
<settingsStack.Screen name="SettingsOne" component={SettingsOneScreen} />
<settingsStack.Screen name="SettingsTwo" component={SettingsTwoScreen} />
</settingsStack.Navigator>
);
}
Then you can easily switch between stacks of Home and Settings
this.props.navigation.navigate('Settings');
https://reactnavigation.org/docs/upgrading-from-4.x/#dismiss
navigation.dangerouslyGetParent().pop();

How to disable drawer inside Stack Navigator nested inside Drawer Navigator?

I have a nested stack navigator inside a drawer navigator and I don't want the user to be able to swipe from the left and open the drawer when they aren't on the first page of the stack navigator. I found some answers but they seem to use some old syntax that isn't used in the official documentation.
// stack navigator
const Items = () => {
const IStack = createStackNavigator()
return (
<IStack.Navigator initialRouteName="Items" screenOptions={{ headerShown: false }} >
<IStack.Screen name="Items" component={ItemSelect} options={{ ...TransitionPresets.SlideFromRightIOS }} />
<IStack.Screen name="Item" component={ItemScreen} options={{ ...TransitionPresets.SlideFromRightIOS }} />
</IStack.Navigator>
)
}
// drawer
const App = () => {
return (
<NavigationContainer theme={MyTheme}>
<StatusBar barStyle="light-content" backgroundColor="#232931" />
<Drawer.Navigator initialRouteName="Items">
<Drawer.Screen name="Items" component={Items} />
</Drawer.Navigator>
</NavigationContainer>
)
}
I want to disable the drawer on the second screen of the stack navigator i.e ItemScreen.
Here's what I ended up doing -
<Drawer.Screen
name="Items"
component={Items}
options={({ route }) => {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Items'
if (routeName == "Item")
return ({swipeEnabled: false})
}}
/>
More about getFocusedRouteNameFromRoute.
It's weird to see no answers anywhere for such a simple scenario.

In react navigation how can we replce drawer screen by stack screen where stack is in drawer? Not same screens (react native)

After successfull loging im moving to this drawer.
const SalesRepDrawerNavigation = () => {
return (
<SalesRepDrawerNavigator.Navigator initialRouteName="SalesRepDashboardStack">
<SalesRepDrawerNavigator.Screen
name="SalesRepDashboardStack"
component={SalesRepDashboardStackNavigation}
options={{
headerTitle: 'Sales Rep Dashboard'
}}
/>
<SalesRepDrawerNavigator.Screen
name="Customers"
component={CustomerListScreen}
/>
<SalesRepDrawerNavigator.Screen
name="SalesOrderView"
component={SalesOrderStackNavigation}
options={{
}}
/>
</SalesRepDrawerNavigator.Navigator>
);
};
const SalesRepDashboardStackNavigation = () => {
return (
<SalesRepDashboardStackNavigator.Navigator key="test" headerMode="none" initialRouteName="SalesRepDashboard">
<SalesRepDashboardStackNavigator.Screen
name="SalesRepDashboard"
component={SalesRepDashboardScreen}
/>
<SalesRepDashboardStackNavigator.Screen
name="CreateSalesOrder"
component={CreateSalesOrderScreen}
/>
</SalesRepDashboardStackNavigator.Navigator>
);
};
then when i go to customer list screen and select a customer I want to move to create sales order screen and at the same time want to remove customer list route. I'm using react native with react navigation v5.
How can I do this?
Use it like:
In DrawerData component you will have to create a drawer item list (a custom component) and for navigation between the screens normally use the navigation.navigate('YOUR_SCREEN_NAME');
Add all your screens in the Stack.Navigator only. Drawer will have only the Home screen.
/*Drawer data is custom drawer item list*/
export function DrawerNav() {
return (
<Drawer.Navigator drawerContent={props => <DrawerData {...props} />}>
<Drawer.Screen name="Home" component={HomeScreen} />
</Drawer.Navigator>
);
}
const RootStackScreen = () => {
return (
<Stack.Navigator>
<>
<Stack.Screen
name="HomeScreen"
component={DrawerNav}
/>
<Stack.Screen
name="FirstScreen"
component={FirstScreen}
/>
<Stack.Screen
name="SecondScreen"
component={SecondScreen}
/>
</Stack.Navigator>
);
}