React Navigation stop back button behaviour on bottom bar - react-native

Hey I am using the createBottomTabNavigator() from React Navigation and I have a custom topbar which I update with my own states. The problem is when I click on a Tab the listener gets called with every navigation so the topbar gets into the wrong state if you for example go to screen 2 the topbar changes for screen 2. When I press on the topbar back button it goes back to screen 1 and changes the topbar to screen 1 topbar. Now if I use the bottombar tab instead to navigate back instead of using the topbar one it keeps the topbar from screen 2. So my question is if I can disable somehow that if I press on the bottom bar that it navigates back to the initial screen 1.
<Tab.Screen
listeners={() => {
switchTab(0);
}}
name="Input"
children={() => (
<SuchStackContainer
ref={suchStackRef}
switchStack={switchStack}
listings={listings}
setListings={setListings}
/>
)}
options={{
tabBarLabel: 'Suche',
tabBarIcon: ({color, size}) => (
<Ionicons name="car" color={color} size={size} />
),
}}
/>

I am supposing you are using Bottom Tabs Navigator from React Navigation. As mentioned in the docs you can override onPress method and navigate it to the desired tab. These can be specified under screenOptions prop of Tab.navigator or options prop of Tab.Screen. Let me know if you still need my help
function MyTabBar({ navigation }) {
return (
<Button
title="Go somewhere"
onPress={() => {
// Navigate using the `navigation` prop that you received
navigation.navigate('SomeScreen');
}}
/>
);
}

Related

React Native - Bottom Tab Navigator - error - Passing an inline function will cause the component state to be lost on re-render?

I have a stack navigator which contains a Bottom tab navigator.
The first tab of the bottom tab navigator further contains a TopTab Navigator .
This is being done to display a Top Tab as well as a Bottom tab.
Each of the other bottom tab screens open a new screen .
Here is the code : ( stack navigator containing Bottom Tab Navigator )
const StackNav = createNativeStackNavigator();
function Main() {
return (
<NavigationContainer>
<StackNav.Navigator>
<StackNav.Screen
name="BottomTab"
component={BottomTabScreen}
options={{
headerTitle: props => <LogoTitle {...props} />,
headerBlurEffect: 'dark',
}}
/>
</StackNav.Navigator>
</NavigationContainer>
);
}
The Bottom Tab Navigator is below ( first tab contains an embedded Top Tab Navigator TopTabScreen to display header , rest of the tabs show tabs on the bottom )
const BottomTabScreen = () => {
return (
<BottomNav.Navigator barStyle={{backgroundColor: '#febe00'}}>
<BottomNav.Screen
name="Home"
component={TopTabScreen}
options={{
tabBarIcon: ({color, size}) => (
<MaterialCommunityIcons name="home" color={color} size={26} />
),
labelStyle: {textTransform: 'none'},
upperCaseLabel: false,
}}
/>
<BottomNav.Screen
name="MyInitiatives"
component={InitiativesScreen}
options={{
tabBarIcon: ({color, size}) => (
<IonIcons name="rocket-outline" color={color} size={26} />
),
}}
/>
<BottomNav.Screen
name="Contact-Whatsapp"
component={() => null}
listeners={{
tabPress: (e) => {
e.preventDefault();
console.log("tabPress tabTwo");
let link = 'https://wa.me/xxx';
Linking.openURL(link);
},
}}
options={{
tabBarIcon: ({color,size}) => (
<FontAwesome name="whatsapp" color={color} size={26} />
),
}}
/>
</BottomNav.Navigator>
);
};
So on bottom tab - three tabs will show :
Home - tab which shows the embedded header
MyInitiatives - shows a separate view when clicked
Contact-Whatsapp - this is a tab which I am trying to show which when clicked should open whats
app and NOT a new screen , so on clicking the tab - I dont want any
'component' to render , rather simply whats app to open
Note - whatsapp is opening but am getting this warning :
WARN Looks like you're passing an inline function for 'component' prop for the screen
'Whatsapp'
(e.g. component={() => <SomeComponent />}). 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.
So questions are :
#1 how do I get around this issue ?
how do I ensure that on clicking on bottom tab - it should open the link ( in this case - whatsapp ) rather than try and open a component / view ?
The reason for the warning is that you are passing a function to the component prop rather than the name of the function. You can put any component name in there as with the e.preventDefault() it will not be opened. You can just create a small dummy component and pass in the name. Even component={View} should do the job.

how to dispatch an action on react navigation custom tab bar component

I want to dispatch an action on of the buttons on custom my tab bar navigator and due to react navigation docs we can not use hooks in custom tab bar component
. Any one has an idea to do that?
first you need to prevent the default action of tab bar , and after perventing you can call your own function or any component.
<Tab.Screen
name="More"
component={Sleep}
options={{
tabBarLabel: <Text style={stylee.Fontfamily}>More</Text>,
tabBarIcon: ({color}) => (
<Icon name="menu-outline" color={color} size={ms(23)} />
),
}}
listeners={({navigation}) => ({
tabPress: event => {
event.preventDefault(); //preventing dafault.
navigation.openDrawer(); //calling custom
},
})}
/>
</Tab.Navigator>
keep in mind , you need to give a component( it's ok if the component is empty or just a text).

How do I add a navigation button to a React Navigation Stack header with nested Bottom Tab Navigator?

I am trying to build a mobile app in react-native and I'm having some problems setting up React Navigation.
What I want to achieve is a Bottom Tab Navigator that Navigates to the 'Home' screen and the 'Profile' Screen. From the 'Home' screen, there should be a button to navigate to the 'Settings' screen in the Header.
I have got to the point where I have a Bottom Tab Navigator that can successfully navigate between the 'Home' and 'Profile' screens, as well as a button on the header for the Settings screen using the Stack navigation header. However, I am having trouble navigating to the 'Settings' screen with this button.
My code for the Stack navigator is:
const MainStackNavigator = () => {
return (
<Stack.Navigator screenOptions={screenOptionStyle}>
<Stack.Screen
name="Home"
component={HomeScreen}
options = { ({navigation}) => ({
title: "Home",
headerStyle: {
backgroundColor: '#ff6600',
},
headerRight: () => (
<Button
onPress={() => navigation.navigate(SettingScreen)}
title="Settings"
color="#fff"
/>
)
})}
/>
<Stack.Screen name="Settings" component={SettingScreen} />
</Stack.Navigator>
);
}
When I click on the Settings button, I get the error:
"The action 'NAVIGATE' with payload undefined was not handled by any navigator.
Do you have a screen named 'SettingScreen'?"
Upon looking for a solution to this error I found this article: Nesting Navigators
It recommends keeping nested navigators to a minimal. Is my method even the right way about going for this UI design? Is there a way to achieve this with only using one navigator?
After some time trying to solve this I found the problem was quite silly of me. navigation.navigate takes the name of the screen to navigate to, but I was giving it the component.
To fix the problem I changed
onPress={() => navigation.navigate(SettingScreen)}
to
onPress={() => navigation.navigate('Settings')}
Add this below your render method!
render () {
const { navigate } = this.props.navigation;
}
And then in the onPress
onPress={() => navigate(SettingScreen)}
Hopefully this helps

Navigate to the screen when Tab on BottomTabNavigator is pressed

I would like to navigate to the screen when the particular tab on the BottomTabNavigator is pressed.
Normally, when the tab is pressed, it navigates to the configured screen automatically. But I don't want to have that behaviour. I want to hide the bottom tab on that screen and provide back feature in the top bar too. I normally use navigation.navigate('routeName') in ReactNavigationStack screens. But I don't know how/where to write this code in the BottomTabNavigator configuration.
For example, I've got the following 5 tabs in the bottom bar. I want to navigate to AddNewScreen when Add button is pressed. I don't know where to put that onPress event. I tried to put it under options and BottomTab.Screen. But still no luck.
I tried to intercept onPress event to use navigation.navigate. But it's not even hit and it always opens the AddNewScreen with the tab bar.
<BottomTab.Navigator initialRouteName={INITIAL_ROUTE_NAME}>
<BottomTab.Screen
name="Home"
component={HomeScreen}
initialParams="Home Params"
options={{
title: 'Home',
tabBarIcon: ({ focused }) => <TabBarIcon focused={focused} name="md-home" iconType="ion" />,
}}
/>
<BottomTab.Screen
name="AddNew"
component={AddNewScreen}
options={{
title: 'Add',
tabBarIcon: ({ focused }) => <TabBarIcon focused={focused} name="md-add-circle" iconType="ion"
onPress={(e) => {
e.preventDefault();
console.log(e)
}} />,
}}
/>
</BottomTab.Navigator>
The Add new screen is always opened with the bottom tab bar.
Questions:
Is there anyway to navigate to specific screen when the tab is
pressed?
Is there anyway to hide the bottom tab bar on that Add New
Screen?
Update:
The Navigation library v6 supports the Listener feature that can be used
<Tab.Screen
name="Chat"
component={Chat}
listeners={{
tabPress: e => {
// Prevent default action
e.preventDefault();
//Any custom code here
alert(123);
},
}}
/>;
You can have a custom functionality in the bottom toolbar using the tabbar button. The code would be like below
<Tab.Screen
name="Settings2"
component={SettingsScreen}
options={{
tabBarButton: props => (
<TouchableOpacity {...props} onPress={() => alert(123)} />
),
}}
/>
This would render a normal bottom tab bar button but the onclick would show the alert, you can replace the code with your navigate or any other code you need.
Also the 'SettingsScreen' component can be a dummy component returning null.
Hope this helps.
You can have a custom functionality
<Tab.Screen
name="Add"
component={View}
listeners={({ navigation }) => ({
tabPress: (e) => {
// Prevent default action
e.preventDefault();
// Do something with the `navigation` object
navigation.navigate("PhotoNavigation"); // Here!!!!!!!!!!!!!!!!!!!!!!!!!!!!
},
})}
/>
<Tab.Screen name="Notifications" component={Notifications} />

How to persist stacked screens when switching between tabs in React Native Navigation

I am using a bottom tab navigator created with createBottomTabNavigator() and passing it several screens, each of which contain their own stack navigation:
const tabNavigator = createBottomTabNavigator({
TradingStack,
LendingStack,
FinanceStack,
AccountBalancesStack,
OtherStack,
},
{
tabBarComponent: props => (
<NavigationFooter />
)
}
);
I am passing it my own custom bottom navigator component called <NavigationFooter />. When tabBarComponent is omitted, a built-in bottom tab navigator is used, however I need more functionality than this built-in solution offers. The issue is, when using my custom tab bar, I lose stack persistence in the individual screens. To clarify, let's say my TradingStack screen has several screens that stack on top of it, and I open one of those. When using the default tab bar, if I switch to the LendingStack screen and then back to TradingStack, I will still be on the stacked screen that was pushed on top of the root TradingStack screen.
However, when using my custom tab bar component, moving between these tabs/screens will always bring me to the root of each one instead of bringing me back to the stacked screen that the tab was on before switching away from it. Here's my custom Footer component:
function Footer({navigation}) {
return(
<View style={styles.tabBarStyle}>
<TouchableOpacity style={styles.tabStyle} onPress={() => navigation.navigate('Trading')} />
<TouchableOpacity style={styles.tabStyle} onPress={() => navigation.navigate('Lending')} />
<TouchableOpacity style={styles.tabStyle} onPress={() => navigation.navigate('Finance')} />
<TouchableOpacity style={styles.tabStyle} onPress={() => navigation.navigate('AccountBalances')} />
<TouchableOpacity style={styles.tabStyle} onPress={() => navigation.navigate('TabScreen')} />
</View>
);
}
const NavigationFooter = withNavigation(Footer);
As you can see, when one of the TouchableOpacity elements is pressed, I use navigation.navigate() to go to the desired screen, but, as mentioned, this brings me to the root of the screen. I understand that this is basically explicitly telling the app to navigate to a root screen of a tab, but I don't know how to make the tab "remember" the stacked screen it was on before I move away, as is the behavior of the default tab bar.
You can pass the name of the actual stack to navigation.navigate instead of the name of the starting screen on that specific tab, and the stacked screens should persist. So, instead of, for example
<TouchableOpacity style={styles.tabStyle} onPress={() => navigation.navigate('Trading')}/> //name of the screen
you should pass it
<TouchableOpacity style={styles.tabStyle} onPress={() => navigation.navigate('TradingStack')}/> //name of the stack
assuming Trading is the starting screen within the TradingStack.