The structure of my react navigation is like this : BottomTabNavigator => Navigator => Components
This is the skeleton of the App.js. The whole application is wrapped up in a bottom tab navigation.
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const BottomTab = createBottomTabNavigator();
function App() {
return (
<NavigationContainer>
<BottomTab.Navigator >
<BottomTab.Screen
name="Main"
component={MyVeranda}
options={{
tabBarLabel: 'Home',
tabBarIcon: //...some icon,
}}
/>
//...some other screens
</BottomTab.Navigator>
</NavigationContainer>
);
}
export default App;
As you can see, inside the bottom tab i have screen name "Main" that uses MyVeranda component. MyVeranda itself is a stack navigator, which have 2 screens components : MyHome and BuyForm
import { createStackNavigator } from '#react-navigation/stack';
const HomeStack = createStackNavigator();
function MyVeranda({ route,navigation }) {
//..some states, props, etc
return (
<HomeStack.Navigator>
<HomeStack.Screen
name="MyHome"
component={MyHome}
options={{/*...some options*/ }}
/>
<HomeStack.Screen
name="BuyItem"
component={BuyForm}
options={{/*...some options*/}}
/>
</HomeStack.Navigator>
);
}
export defaul MyVeranda;
MyVeranda is a parent of MyHome and BuyForm, both are just 2 simple functional components
function MyHome({navigation}){
//..some states, props, etc
return (
<ScrollView contentContainerStyle={{/*...some styling*/}}>
//...some components
</ScrollView>
);
}
function BuyForm({navigation}){
//..some states, props, etc
return (
<ScrollView contentContainerStyle={{/*...some styling*/}}>
//...some components
</ScrollView>
);
}
My question is : how to hide the root bottom tab navigator when navigating to BuyForm component, but not when go to MyHome component?
Based from answer of this question, i know that i can hide the bottom tab if i put this line navigation.setOptions({ tabBarVisible: false }) in MyVeranda component
function MyVeranda({ route,navigation }) {
//..some states, props, etc
navigation.setOptions({ tabBarVisible: false })//this hide the bottom tab navigator
return (
//...
)
}
But this hide the bottom tab entirely when i am at both MyHome and BuyForm component.
Calling navigation.setOptions({ tabBarVisible: false }) in BuyForm seems to do nothing
function BuyForm({ route,navigation }) {
//..some states, props, etc
navigation.setOptions({ tabBarVisible: false }) //this do nothing
return (
//...
)
}
So my guess is i have to call navigation.setOptions({ tabBarVisible: false }) inside MyVeranda when BuyForm is the active screen, but i cannot the proper syntax to get the current active screen from a stack navigator component?
Here is the answer
React Navigation how to hide tabbar from inside stack navigation
Indeed you can use setOptions on the navigation to set custom options. Although it's recommended to rather reorganize the navigation structure. This might be possible if you create a StackNavigator not nested with the TabBar.
React.useLayoutEffect(() => {
navigation.setOptions({ tabBarVisible: false });
}, [navigation]);
Related
React native navigation from stack navigator is not being passed down to the Restaurantsscreen function
This is the function which has to use naviagtion but it cant
export const RestaurantsScreen = ({navigation}) => (
<SafeArea>
<View style={{"backgroundColor": "rgba(255, 255, 255, 255)"}}>
<FlatList
data={[
{ name: 1 }
]}
renderItem={(item) =>{return (
<Pressable onPress={ ()=>console.log(navigation)}>
<RestaurantInfoCard />
</Pressable>
);
}}
and these are the functions where stack navigator is defined
function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Home">
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={Screentwo} /> // this screen is currently loaded and the screentwo is importing the Restaurants screen function inside it as a component
</Stack.Navigator>
</NavigationContainer>
);
}
Kindly help
thanks
You have two solutions :
Past the navigation props from the Screentwo to you're child component RestaurantsScreen
Used the useNavigation hooks directly in your RestaurantsScreen component (see link for this hook : https://reactnavigation.org/docs/use-navigation/)
Example of the first solution:
//Screentwo inherit navigation props direclty, as this component is called in your Stack.Navigator
export const Screentwo = ({navigation}) => {
return(
//As RestaurantsScreen is not called directly in your Stack.Navigator,
//this component will not inherit navigation props,
//you need to past manually navigation props from Screentwo in this way
<RestaurantsScreen navigation={navigation} />
)
}
Example of the second solution with useNavigation :
//Directly in your RestaurantsScreen component
import { useNavigation } from '#react-navigation/native';
//No need to import navigation props
export const RestaurantsScreen = () => {
//Call useNavigation, and navigation can be used in the exact same way as if you past it as a props
const navigation = useNavigation()
return (
<SafeArea>
the rest of your code here
</SafeArea>
)
}
I am developing an android app with React Native and Native-Base.
In my component button i can't get onpress to work to change page.
import React from "react";
import { StyleSheet, View, TouchableOpacity } from "react-native";
import { NativeBaseProvider, Button, Text } from "native-base";
const ButtonAPP = (props) => {
//const linkBottom = props.linkBottom;
const textBottom = props.textBottom;
return (
<NativeBaseProvider>
<View style={styles.viewButtonAPP}>
<Button
shadow={2}
style={styles.buttonAPP}
onPress={() => props.navigation.navigate("Home")}
>
<Text style={styles.textButton}>{textBottom}</Text>
</Button>
</View>
</NativeBaseProvider>
);
};
const styles = StyleSheet.create({
styles....
});
export default ButtonAPP;
THe error is:
navigate is not defined
I don't understand why for pages in my app it works but for a component like the button it doesn't
The navigation object will be passed as props by the navigation framework only to components that are defined as screens inside a navigator. If this is not the case, then the navigation object will not be passed to this component.
You have three choices.
1) Use the useNavigation hook
const ButtonAPP = (props) => {
const navigation = useNavigation();
...
<Button
shadow={2}
style={styles.buttonAPP}
onPress={() => navigation.navigate("Home")}
>
}
2) Pass the navigation object as a prop from a parent that is a screen defined in a navigator
Assume that ScreenA is a screen that is defined in a navigator, e.g. a Stack. This would look like the following snippet.
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="ScreenA" component={ScreenA} />
</Stack.Navigator>
);
}
Assume that ButtonApp is used as a child in ScreenA. Then, we can pass the prop manually, since the framework will not do this.
const ScreenA = (props) => {
return <ButtonApp navigation={props.navigation} />
}
const ButtonAPP = (props) => {
...
<Button
shadow={2}
style={styles.buttonAPP}
onPress={() => props.navigation.navigate("Home")}
>
}
3) Let ButtonApp be a screen inside a navigator
This choice usually does only make sense for actual views in the application and not for individual components. However, this is still an option.
Using the same example as in 2). However, this will work with any navigator.
const Stack = createStackNavigator();
function MyStack() {
return (
<Stack.Navigator>
<Stack.Screen name="ButtonApp" component={ButtonAPP} />
</Stack.Navigator>
);
}
Accessing the navigation object via props as you have tried initially works in this case as well.
I'm working on a bigger app and have some lag when opening the drawer. It takes about 1 second for the drawer animation to begin.
I looked into it with react profiler and saw that the drawer is rendered and the current screen is rerendered before the drawer opens. This makes things feeling slow, I would not suspect the current screen to rerender.
Here is how my pseudo navigation stack looks topdown:
//Toplevel
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const RootStack = createStackNavigator();
<NavigationContainer ref={navRef} theme={navTheme} linking={linking}>
<RootStack.Navigator>
{loggedIn ? (
<RootStack.Screen component={inBetweenComponent} /> // => calls drawer
) : (
// auth screen
)}
</RootStack.Navigator>
</NavigationContainer>
const DrawerNavigator = () => {
const Drawer = createDrawerNavigator();
const route = useRoute();
const activeRoute = getFocusedRouteNameFromRoute(route);
return (
<Drawer.Navigator
drawerContent={props => <DrawerContent {...props} activeRoute={activeRoute} />}
>
<Drawer.Screen name="Notifications" component={NotificationsNavigator} />
<Drawer.Screen name="Conversations" component={ConversationsNavigator} />
</Drawer.Navigator>
}
const DrawerContent = ({ activeRoute }) => {
const navigation = useNavigation();
return (
<DrawerContentScrollView>
<TouchableNative onPress={goToNotifications} >..some text...</TouchableNative>
<TouchableNative onPress={goToConversations} >..some text...</TouchableNative>
</DrawerContentScrollView>
)
}
const NotificationsNavigator = ({ navigation }) => {
const Stack = createStackNavigator();
return (
<Stack.Navigator
screenOptions={{
headerLeft: () => <HeaderLeft navigation={navigation} />, // -> contains open drawer button
}}
initialRouteName="Notifications"
>
<Stack.Screen
name="Notifications"
component={NotificationsScreen}
/>
</Stack.Navigator>
);
}
const HeaderLeft = () => {
const navigation = useNavigation();
const openNavigation = () => {
navigation.openDrawer();
};
<TouchableNative /*someicon*/ onPress={openNavigator} />
I would like to know:
Is it normal that the current active screen rerenders before the drawer opens?
If normal, Is there any way around rerendering the current active screen? I tried a solution with useMemo + areEqual function with isDrawerOpen (useIsDrawerOpen) send from the parent. But that is not consistent and often gives wrong or undefined values.
Any other pointers to why this stack might be slow are greatly appreciated.
Thanks in advance!
The reason your screen is re-rendering when you open the drawer is because you're defining the drawer in the DrawerNavigator component.
This means every time React Navigation wants to find the drawer is has to reload not only the current screen, but every screen in the drawer.
Moving const Drawer = createDrawerNavigator(); outside of DrawerNavigator should solve your problem.
With React Navigation 5, I want to open Drawer when I click on bottom tab navigator (I use material bottom navigator).
I manage to create the bottom tabs buttons and click on them, the home page opens for both tabs (GymIndexScreen or FoodIndexScreen).
When I am on the home pages (GymIndexScreen or FoodIndexScreen), I can open the different Drawers with my fingers (GymDrawerNavigator and FoodDrawerNavigator ) : everything works fine.
Question :
I want the drawers to open / close (toggle) automatically when I click the bottom tabs buttons, without having to open them with my fingers.
App.js :
import { NavigationContainer } from '#react-navigation/native'
const App = () => {
return (
<NavigationContainer>
<BottomTabNavigator />
</NavigationContainer>
)
}
BottomTabNavigator.js :
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs'
const Tab = createMaterialBottomTabNavigator()
const BottomTabNavigator = (props) => {
return (
<Tab.Navigator>
<Tab.Screen
name="Gym"
component={GymDrawerNavigator}
options={{
tabBarLabel: "Musculation",
)}
/>
<Tab.Screen
name="Food"
component={FoodDrawerNavigator}
options={{
tabBarLabel: "Alimentation",
)}
/>
</Tab.Navigator>
)
}
GymDrawerNavigator.js :
import { createDrawerNavigator } from '#react-navigation/drawer'
const Drawer = createDrawerNavigator()
const GymDrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen
name="Gym"
component={GymStackNavigator}
/>
</Drawer.Navigator>
)
}
GymStackNavigator.js :
import { createStackNavigator } from '#react-navigation/stack'
const Stack = createStackNavigator()
const GymStackNavigator = () => {
return (
<Stack.Navigator initialRouteName="GymIndex">
<Stack.Screen
name="GymIndex"
component={GymIndexScreen}
}}
/>
<Stack.Screen
name="GymExerciseIndex"
component={GymExerciseIndexScreen}
}}
/>
... list of screens
If I understood your problem correctly you want to open the drawer automatically when you navigate to the screen?
Add this to the screen components you wish to open the drawer when navigated to.
import {useEffect} from 'react'
...
useEffect(()=>{
navigation.addListener('focus', () => {
// when screen is focused (navigated to)
navigation.openDrawer();
});
},[navigation])``
This answer helped me.
Just use the listeners prop to preventDefault behaviour and then open the drawer.
<Tabs.Screen
name={"More"}
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault();
navigation.openDrawer();
}
})}
component={Home}
/>
I'm trying to implement a drawer navigation alongside a tab navigation, but I can't seem to wrap my head around how that would be achieved.
The tab would have 5 items, and should be present on all screens. The 5th tab should open a drawer with more menu items.
Clicking on any of the drawer's menu items should of course show those specific screens, but the tab navigation should be still be present.
In the code below the bottom tab bar (which could be replaced by a top tab navigator) is always present.
The last tab contains the the drawer. It doesn't open by default, but this can be achieved by a this.props.navigation.openDrawer() request.
import React from 'react';
import { View, Text } from 'react-native';
import { createBottomTabNavigator, createDrawerNavigator } from 'react-navigation';
const buildScreen = name => {
class Screen extends React.Component {
render() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>{name}</Text>
</View>
);
}
}
return Screen;
}
const DrawerScreen = createDrawerNavigator(
{
Child1: buildScreen("DrawerChild 1"),
Child2: buildScreen("DrawerChild 2"),
},
{
// optional drawer options
// cfr. https://reactnavigation.org/docs/en/drawer-based-navigation.html
}
);
export default createBottomTabNavigator({
Tab1: buildScreen("Tab1"),
Tab2: buildScreen("Tab2"),
Tab3: buildScreen("Tab3"),
Tab4: buildScreen("Tab4"),
DrawerScreen,
});
I know this is an old question, but for people still looking for an answer, this is how I implemented mine.
The Drawer Navigation has to be your main navigation. Your tab navigation is nested inside it.
import React from 'react';
import { createDrawerNavigator } from '#react-navigation/drawer';
import AppNavigator from './AppNavigator';
const Drawer = createDrawerNavigator();
function DrawerNavigation() {
return (
<Drawer.Navigator>
<Drawer.Screen name='Home' component={TabNavigation} />
</Drawer.Navigator>
);
}
export default DrawerNavigation;
In your Tab Navigation, the tab which you choose to show your Drawer Navigation (in your case the fifth tab) would have a listener prop which fires a function on tabPress, toggling the drawer navigation
import React from 'react';
import { Text } from 'react-native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
const Home = () => (
<Text> Hello Home</Text>
);
const Menu = () => {
return null;
};
function TabNavigation(props) {
return (
<Tab.Navigator>
<Tab.Screen
name='Home'
component={Home}
/>
<Tab.Screen
name='Menu'
component={Menu}
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault();
navigation.toggleDrawer();
},
})}
/>
</Tab.Navigator>
);
}
export default TabNavigation;
You can create a stackNavigator with two screens and a materialTabNavigator inside:
const SomeStack= createStackNavigator({
Start: StartStack,
HomeScreenTab,
}
and then:
HomeScreenTab: {
screen: HomeScreenTabTab
}
with:
export default createMaterialTopTabNavigator(
{
Home: {
screen: HomeStack,
navigationOptions: {
tabBarAccessibilityLabel: 'Tela Inicial do APP',
tabBarLabel: ({ tintColor }) => <LabelTitle tintColor={tintColor}
label="Start" />,
tabBarIcon: ({ tintColor }) => (
<View style={styles.containerIcon}>
<FontAwesome color={tintColor} name="home" size{icons.iconMd}
light />
</View>
),
},
},
...SomeOtherTabs
}
You put the content of your screens on the screen property and so on.