React Navigation 5 : Implementing a custom header on a BottomTabNavigator - react-native

I am using a BottomTabNavigator with 2 screens but I also want to use a custom header, which I imported, to each one of them. I have tried set an option to the Tab.Navigator by adding a setOptions in it:
const Tab = createBottomTabNavigator();
export default function App() {
return(
<NavigationContainer >
<Tab.Navigator setOptions={{
headerTitle: <Header />
//</Header> was imported
}}>
<Tab.Screen
name="HomeScreen"
component={HomeScreen}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({ color, size }) => (
<AntDesign name="home" color={Colors.amarelo} size={30} />
)
}}
/>
<Tab.Screen
name="GroupScreen"
component={GroupScreen}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({ color, size }) => (
<AntDesign name="car" color={Colors.amarelo} size={30} />
)
}}
/>
</Tab.Navigator>
</NavigationContainer>
)
}
However, my attempt was unsuccessful. I read that docs for React-Navigation 5 but I haven't found how to implement a custom header on a BottomTabNavigator

Bottom Tab navigator does not have a header. To do this you have to use stack Navigator inside each tab of the bottom tab navigator. So you need to create a stack navigator that have "HomeScreen" as screen, same for GroupScreen. Then, in the bottom tab navigator use the stack navigators as component for tab.screen.
Than you can customize headers of bottom tab navigator.
I could post a short code if it helps you

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).

Disable animation for a custom header in React Navigation

I would like to disable the screen animation for the header part of the Stack Navigator.
I have a common custom Header defined in the Stack Navigator via screenOptions.
And have default animations for screen transitions.
I want to make sure the animation happens only to the screen and not to my header component.
Since the header will a static content.
I've also tried making the headerMode as screen and float but that did not help.
I wanted to see if there is a property similar to animationEnabled but for the header component.
<Stack.Navigator
screenOptions= {{
headerMode: 'screen',
animation: 'fade',
header: (props) =>
<Header {...props} />
}}>
// Rest of my screens
</Stack.Navigator>
What you could do is completely separate the header from your Navigator, and use a ref to control navigation from it. Something like this:
const App = () => {
const navigationRef = useNavigationContainerRef()
return (
<View>
<Text>This header won't animate!</Text>
<Text onPress={() => navigationRef.navigate('Home')}>Link</Text>
</View>
<NavigationContainer ref={navigationRef}>
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Other" component={OtherScreen} />
</Stack.Navigator>
</NavigationContainer>
)
}

React navigation 5.x / React native – bottom-tabs navigation with same instance of component

I want to reuse the same instance of one component in two tabs (bottom bar tabs).
created with const Tab = createBottomTabNavigator();
Tab stack:
<Tab.Navigator
tabBarOptions={{
activeTintColor: Colors.tabs.active,
inactiveTintColor: Colors.tabs.inactive,
}}>
<Tab.Screen
name="NavigationMap"
component={Map}
options={{
tabBarLabel: 'Navigation',
}}
/>
<Tab.Screen
name="DiscoveryMap"
component={Map}
options={{
tabBarLabel: 'Discover',
}}
/>
<Tab.Screen
name="Other"
component={OtherComponent}
options={{
tabBarLabel: 'Other',
}}
/>
</Tab.Navigator>
I want to have the same behavior than in the Google Maps application on Android with the "Explore" and "Commute" tabs: stay in the same screen with a different state. I do not want to reload completely my map between the 2 tabs (and have independant zoom levels, center, ...).
Note: I cannot achieve that behavior with the tabPress method.
You can add focus listeners to both screens. Here, you can set the context or global state that can be accessed by the HomeNavigation component and change the behaviour.
<Tab.Screen
name="Home"
component={HomeNavigation}
listeners={{
focus: () => console.warn('focused 1'),
}}
/>
<Tab.Screen
name="Home2"
initialParams={{testing: true}}
component={HomeNavigation}
listeners={{
focus: () => console.warn('focused 2'),
}}
/>

React native: How can I have multiple drawer navigator links point to screens within the same stack navigator

I am new to react native and I haven't seen this question asked by anyone or haven't found a way around this.
Using react navigation 5 with expo.
Currently I have a the following app structure:
Stack navigator inside of drawer navigator.
Example of page structure:
Drawer Navigator ( links ):
Home (RouteStack)
Screen 1
Screen 2
Screen 3
RouteStack( screens) :
Home ( initial route )
Screen 1
Screen 2
Screen 4
How can I get Screen 1/Screen 2 link in drawer navigator load RouteStack: Screen 1/Screen 2?
These links are provided to easily jump to the required screen.
Need some guidance on how to achieve this.
I have thought of the possibility of drawer inside of stack, but there are screens inside of drawer that may not be listed in the stack. Hence, went with stack inside of drawer.
I have also tried to do a navigation.navigate(route.name) inside of RouteStack
Sample code:
Drawer navigator:
<NavigationContainer>
<Drawer.Navigator drawerContent={(props, navigation) => <CustomDrawerContent {...props} {...navigation} />}>
<Drawer.Screen name="Home" component={RouteStack} />
<Drawer.Screen name="MyItems" component={RouteStack} />
<Drawer.Screen name="ContactRep" component={RouteStack} />
<Drawer.Screen name="Settings" component={SettingInfo} />
</Drawer.Navigator>
</NavigationContainer>
Stack navigator (RouteStack) looks like this:
<Stack.Navigator
initialRouteName="Home"
screenOptions={{ gestureEnabled: false, headerTitleAlign: 'auto' }}
// headerMode="float"
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
title: '',
headerStyle: {
backgroundColor: '#fff',
},
headerTintColor: '#000',
headerTitleStyle: {
fontWeight: 'bold'
},
headerLeft: props => <HeaderLeftMenu {...props} />,
headerRight: props => <HeaderRightMenu {...props} />,
headerTitle: props => <HeaderTitle {...props} />
}}
/>
<Stack.Screen
name="ContactRep"
component={ContactRep}
options={{ headerTitle: props => <HeaderTitle {...props} /> }}
/>
<Stack.Screen
name="MyItems"
component={MyItems}
options={{ headerTitle: (props, navigation) => <HeaderTitle {...props} /> }}
/>
</Stack.Navigator>
Thanks in advance and help is appreciated.
Your method is fine. But to clarify your ideas I will give you an example.
Assume I have a main Drawer. In that drawer I can navigate to 2 different screens. Inside those screens, I can navigate and do diferent things (like going to sub-screens), but never go outside the drawer.
To do this, we would have to created nested navigators. This meaning, one type of navigator if going to be inside another one. In our case of example:
<Papa Drawer>
<Screen 1 component={StackSon1}>
<Screen 2 component={StackSon2}>
<Papa Drawer>
And then StackSon1, for example, will look like this:
StackSon = () => {
return (
<Stack.Navigator>
<Stack.Screen>
<Stack.Screen>
...
)
}
React Navigation will also handle every drawer separately, meaning that you don't have to worry about the user creating an infinite chain of open screens.
Also, remember that, when we Nest navigators using a function (like I did) we must use return (or the simplified version of return with just parenthesis)
Hope it helps.