opening drawer by pressing on bottom tab not working - react-native

I am trying to toggleDrawer on TabPress in BottomTab, but when I press on tab I get the following error navigation.toggleDrwer() is not a function.
This is my code:
export default function BottomTab() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={DrawerTab} style={styles} listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault()
navigation.toggleDrawer()
},
})} />
</Tab.Navigator>
</NavigationContainer>
);
}

i have manage to find the answer. So you actually have to use dispatch.
<Tab.Screen name="Home" component={DrawerTab} style={styles} listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault()
navigation.dispatch(DrawerActions.toggleDrawer())
},
})} />

Related

React-Native, Navigation, Drawers and passing inline functions with props

I have a Drawer Navigator nested inside a Stack navigator. It all works fine but I get the following warning:-
Looks like you're passing an inline function for 'component' prop for the screen 'Home' (e.g. component={() => }). Passing an inline function will cause the component state to be lost on re-render and cause perf issues since it's re-created every render. You can pass the function as children to 'Screen' instead to achieve the desired behaviour.
Here is the code.
const [isAuthenticated, setIsAuthenticated] = React.useState(false);
const [initialRoute, setInitialRoute] = React.useState("");
const handleSignOut = () => {
// TODO implement sign out mechanism
};
return (
<NavigationContainer>{
<Stack.Navigator initialRouteName="Sign In" >
{isAuthenticated ? (
<Stack.Screen name="Home"
component={() => <HomeDrawer initialRoute={initialRoute} handleSignOut={handleSignOut}/>}
options={({ route, navigation }) => ({
headerTitle: getFocusedRouteNameFromRoute(route),
headerLeft: () => (
<Button
title="Menu"
onPress={() =>
navigation.dispatch(DrawerActions.toggleDrawer())
}
/>
),
headerRight: () => (
<Button onPress={handleSignOut} title="Sign Out" />
),
})}
/>
) : (
...
I fixed the warning by moving the component, but now the Drawer has stop working completely and I get this error:-
The action 'TOGGLE_DRAWER' was not handled by any navigator.
Is your screen inside a Drawer navigator?
Here is the new code.
const [isAuthenticated, setIsAuthenticated] = React.useState(false);
const [initialRoute, setInitialRoute] = React.useState("");
const handleSignOut = () => {
// TODO implement sign out mechanism
};
const Draw = () => {<HomeDrawer initialRoute={initialRoute} handleSignOut={handleSignOut}/>};
return (
<NavigationContainer>{
<Stack.Navigator initialRouteName="Sign In" >
{isAuthenticated ? (
<Stack.Screen name="Home"
component={Draw}
options={({ route, navigation }) => ({
headerTitle: getFocusedRouteNameFromRoute(route),
headerLeft: () => (
<Button
title="Menu"
onPress={() =>
navigation.dispatch(DrawerActions.toggleDrawer())
}
/>
),
headerRight: () => (
<Button onPress={handleSignOut} title="Sign Out" />
),
})}
/>
) : (
...
I'm new to React-Native and on a steep learning curve. I've read a bunch of tutorials but stumped on this one. I can see I need to pass initialRoute at least, but not sure how to do that. A fix with an explanation would be great.
I found the answer in the end. It was pretty straight forward, but not when there are so many things to learn at once :-) When I moved the component like so it all worked.
<Stack.Screen name="Home"
options={({ route, navigation }) => ({
headerTitle: getFocusedRouteNameFromRoute(route),
headerLeft: () => (
<Button
title="Menu"
onPress={() =>
navigation.dispatch(DrawerActions.toggleDrawer())
}
/>
),
headerRight: () => (
<Button onPress={handleSignOut} title="Sign Out" />
),
})}
>
{(props) => (
<HomeDrawer {...props} initialRoute={initialRoute} handleSignOut={handleSignOut} />
)}
</Stack.Screen>
<Stack.Screen name="TEST" component={TestScreen}
screenOptions={{ headerShown: true }}
options={{ headerTitle: "Hello" }}
/>

React Native Drawer Navigation show headerLeft

I have a Drawer navigator which is inside a Stack navigator and I'd like to display a header. Currently I can display everything I want however because the header is defined at the Stack level the navigation inside the header is stack level not drawer level which is preventing me from opening the drawer.
Root stack
<Stack.Navigator
initialRouteName={"Splash"}
screenOptions={{}}
component={SplashScreen}
>
{ auth ?
<Stack.Screen name="Drawer" component={DrawerStack} options={({ navigation }) => ({
title: 'My App',
headerLeft: () => (
<HeaderLeft navigation={ navigation } />
),
headerRight: () => (
<HeaderRight navigation={ navigation } />
),
headerTitleAlign: 'center',
headerTintColor: 'white',
headerStyle: {
backgroundColor: '#5742f5'
},
})} />
:
<Stack.Screen name="Auth" component={AuthStack} options={{
headerShown: false
}}/>
}
</Stack.Navigator>
Drawer stack
<Drawer.Navigator options={{
headerShown: true,
headerLeft: () => (
<HeaderLeft navigation={ navigation } />
),
}}>
<Drawer.Screen
name="Conversations"
options={{
title: props.title,
}}
component={ChatListScreen}
/>
<Drawer.Screen
name="ChatRoom"
options={{
drawerLabel: () => null,
title: null,
drawerIcon: () => null
}}
component={ChatRoomScreen}
/>
</Drawer.Navigator>
Note in the drawer navigator the line with headerLeft does nothing and is there to show where I attempted to put it thinking it would work. I did think it might be overlaying the stack one so I commented out the stack one and it didn't work.
HeaderLeft
export default function HeaderLeft ({ navigation }) {
const openMenu = () => {
navigation.toggleDrawer();
}
return (
<View style={styles.header}>
<Icon name='menu' onPress={openMenu} size={28} style={styles.icon} color="white"/>
</View>
)
}
My question is how can I refactor this to enable me to have the HeaderLeft component work to open the drawer. I will be adding more screens so ideally something I don't have to pass to each screen but if that is what works I am good with it too.
Options in DrawerStack not work. I modified it:
<Drawer.Navigator
screenOptions={{
headerLeft: () => <HeaderLeft />,
}}>
// ...
</Drawer.Navigator>
And a little change in HeaderLeft:
import { useNavigation } from '#react-navigation/native';
function HeaderLeft() {
const navigation = useNavigation();
const openMenu = () => {
navigation.toggleDrawer();
};
// render your Button
}
Demo: https://snack.expo.dev/#pqv2210/0d613b

Can i disable tabBarBadge on click of tab screen?

I have used Tab.Navigator to display 4 bottom navigation icons, one of which is Notification Icon. I want to disable the tabBarBadge option when i click on it. How to do it?
Here is the code I used
<Tab.Screen
name="Notifications"
component={Notifications}
options={{
tabBarIcon: ({ focused }) =>
focused ? (
<Notification_icon />
) : (
<Notification_icon_inactive />
),
tabBarBadge: 2,
}}
/>
There is a way to do this using NavigationContainer's ref and controlling the visibility of the badges through some state variable. Listen for state event on the ref. In the handler of this event you get the name of the current route using ref.current?.getCurrentRoute()?.name. If this name is equal to the Notifications screen name then you set notificationBadgeVisible to false thus hiding the badges. Check the following code.
const App = () => {
const ref = React.useRef<NavigationContainerRef>(null);
const [notificationBadgeVisible, setNotificationBadgeVisible] = React.useState(true);
const navigationStateChangeHandler = () => {
if (ref.current?.getCurrentRoute()?.name === 'Notifications') {
setNotificationBadgeVisible(false);
}
}
React.useEffect(() => {
ref.current?.addListener('state', navigationStateChangeHandler);
return () => { ref.current?.removeListener('state', navigationStateChangeHandler); }
});
return (
<NavigationContainer ref={ref}>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen
name="Notifications"
component={Notifications}
options={{
tabBarIcon: ({ focused }) =>
focused ? (
<Notification_icon />
) : (
<Notification_icon_inactive />
),
tabBarBadge: (notificationBadgeVisible ? 2 : undefined)
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
};

onPress to navigate on a screen not navigating

I am a beginner in React Native and I would like to navigate on a screen when I press the Button of my Navigator screen but it's not working.
Because when I press the Button in TabOneStack.Screen nothing is happening I don't understand, I would like to navigate to TabTwoScreen. I use React Navigation 5.6.1.
const BottomTab = createBottomTabNavigator<BottomTabParamList>();
export default function BottomTabNavigator() {
return (
<BottomTab.Navigator
initialRouteName="TabOne">
<BottomTab.Screen
name="TabOne"
component={TabOneNavigator}
/>
<BottomTab.Screen
name="TabTwo"
component={TabTwoNavigator}
options={{
tabBarLabel: 'Autour de moi',
tabBarIcon: ({ color }) => <TabBarIcon name="ios-navigate" color={color} />,
}}
/>
</BottomTab.Navigator>
);
}
const TabOneStack = createStackNavigator<TabOneParamList>();
function TabOneNavigator() {
return (
<TabOneStack.Navigator>
<TabOneStack.Screen
name="TabOneScreen"
component={TabOneScreen}
options={({ navigation }) => ({
headerTitle: 'Rejoindre', headerRight: () => (
<Button onPress={() => navigation.navigate('TabTwoScreen')}
icon={
<Icon
name='ios-log-in'
type='ionicon'
size={15}
color="white"
/>
}
/>
),
})}
/>
</TabOneStack.Navigator>
);
}
const TabTwoStack = createStackNavigator<TabTwoParamList>();
function TabTwoNavigator() {
return (
<TabTwoStack.Navigator>
<TabTwoStack.Screen
name="TabTwoScreen"
component={TabTwoScreen}
options={{ headerTitle: 'Autour de moi' }}
/>
</TabTwoStack.Navigator>
);
}
Why when I press the Button in TabOneStack.Screen nothing is happening?
Thank you in advance
You need to use props for navigation and you can try this
options={({ navigation }) => ({
headerTitle: 'Rejoindre', headerRight: props => (
<Button onPress={() => props.navigation.navigate('TabTwoScreen')}

React Native - Header Title on Material Bottom Tabs on Navigation 5

I have a Material Bottom Tabs into a Stack Navigator in Navigation 5.
It is suppose dto get the title and route it throught a function but doesn't work.
function:
function getHeaderTitle(route) {
const routeName = route.state
? route.state.routes[route.state.index].name
: route.params?.screen || 'Home';
switch (routeName) {
case 'Favorites':
return 'Favorites';
case 'Info':
return 'Info';
}
}
materialBottomTabs
const NavTab = () => {
return (
<Tab.Navigator>
<Tab.Screen
name="Favorites"
component={FavoritesScreen}
options={({route}) => ({
tabBarLabel: 'Fav',
tabBarIcon: () => IconFav(),
})}
/>
<Tab.Screen
name="Info"
component={InfoScreen}
options={({route}) => ({
headerTitle: 'Info',
tabBarLabel: 'Info',
tabBarIcon: () => IconInfo(),
})}
/>
</Tab.Navigator>
stackNavigator
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={NavTab}
options={({route}) => ({
headerTitle: getHeaderTitle(route),
})}
/>
<Stack.Screen
name="Detail"
component={ItemDetailScreen}
/>
</Stack.Navigator>
</NavigationContainer>;
I easly set the title of a screen listed in the Stack.
Example:
const ItemDetailScreen = ({route, navigation}) => {
useEffect(() => {
navigation.setOptions({
headerTitle: item.title,
});
}, []);
But I can't set the title of the screens into the material bottom tabs. They always have the name set into the stack navigator. How can I do it?
options={({route}) => ({
headerTitle: getHeaderTitle(route),
})}
i think it should be:
options={({route}) => ({
title: getHeaderTitle(route),
})}