Dynamic initialRouteName in BottomTabNavigator - React Navigation v5 - react-native

I want to set a dynamic initialRouteName on my TabNavigator (not even sure if this is the right place to do it), let me explain more clearly.
I have a BottomTabNavigator with 4 items : Home (HomeStack), Explorer (ExplorerStack), subscriptions (SubscribeStack) & contact (contactStack). When the app load, with the token I was redirecting the user to my MainNavigator (so my BottomTabNavigator) with the initialRouteName Home.
<Stack.Navigator headerMode="none">
{!token ? (
<Stack.Screen
isSignout={isSignout}
name="AuthStack"
component={AuthNavigator}
/>
) : (
<Stack.Screen name="HomeStack" component={MainNavigator} />
)}
</Stack.Navigator>
What I want to do now is : when the user is not null and have no subscriptions I want to redirect him directly to the Explorer Tab and not the Home Tab.
// MainNavigator.js
const MainNavigator = (props) => {
const [loading, setLoading] = useState(true);
const [subs, setSubs] = useState([]);
const [fetchData, {data: meData}] = useLazyQuery(USER_ME_QUERY);
useEffect(() => {
fetchData();
if (meData) {
setSubs(meData.me.subscribes);
setLoading(false);
}
}, [meData]);
if (loading) {
return <Loading />;
}
return (
<Tab.Navigator
initialRouteName={subs.length > 0 ? 'Home' : 'Explorer' }
tabBarOptions={{
shadowColor: 'transparent',
showLabel: false,
style: {
backgroundColor: dark ? theme.color.secondary : theme.color.white,
elevation: 0, // for Android
borderTopColor: dark ? '#46505C' : theme.color.lightBorder,
},
}}>
<Tab.Screen
name="Home"
component={HomeStack}
options={{
tabBarIcon: ({focused}) =>
focused ? (
<Icon size={22} name="home" color={theme.color.primary} />
) : (
<Icon size={22} name="home" color={theme.color.lightGrey} />
),
}}
/>
<Tab.Screen
name="Explorer"
component={ExplorerStack}
options={{
tabBarIcon: ({focused}) =>
focused ? (
<Icon size={22} name="columns" color={theme.color.primary} />
) : (
<Icon size={22} name="columns" color={theme.color.lightGrey} />
),
}}
/>
<Tab.Screen
name="Subscribe"
component={SubscribeStack}
options={{
tabBarIcon: ({focused}) =>
focused ? (
<Icon size={22} name="sliders" color={theme.color.primary} />
) : (
<Icon size={22} name="sliders" color={theme.color.lightGrey} />
),
}}
/>
<Tab.Screen
name="Contact"
component={ContactStack}
options={{
tabBarIcon: ({focused}) =>
focused ? (
<Icon size={22} name="mail" color={theme.color.primary} />
) : (
<Icon size={22} name="mail" color={theme.color.lightGrey} />
),
}}
/>
</Tab.Navigator>
);
};
export default MainNavigator;
Is there a simple way to achieve this ? Any help would be appreciated x
If you need more infos, feel free to ask :)
Thanks in advance x

Related

Turning off the drawer header in a specific screen in bottom tab navigation in drawer navigation in react native

I have a very big problem I am using two navigations in one app the first one is the drawer navigator this is how I use the drawer navigator:
<Drawer.Navigator
screenOptions={{
drawerStyle: {
backgroundColor: 'white',
},
}}
drawerContent={props => <CustomSidebarMenu {...props} />}
drawerContentOptions={{
activeTintColor: '#e91e63',
activeBackgroundColor: 'red',
itemStyle: {marginVertical: 20},
}}>
<Drawer.Screen
options={{
headerShown: true,
headerTitle: () => (
<Image
style={{height: 150, width: 100, resizeMode: 'contain'}}
source={require('../assets/images/referans2.png')}
/>
), // Title to appear in the header
headerRight: ({navigation, scene}) => (
// Custom component to appear on the right side of the header
<View style={{flexDirection: 'row'}}>
<Pressable>
<Ionicons
name="notifications-outline"
size={30}
color={'black'}
/>
</Pressable>
<Pressable style={{marginHorizontal: 10}}>
<Ionicons
name="chatbubbles-outline"
size={30}
color={'black'}
/>
</Pressable>
</View>
),
}}
name="Ana Sayfa"
component={Main}
/>
<Drawer.Screen
options={{headerShown: true}}
name="Şiparişlerim"
component={MyOrders}
/>
<Drawer.Screen name="Adreslerim" component={AddressesScreen} />
<Drawer.Screen name="Üyelik Bilgilerim" component={AccountInfoScreen} />
</Drawer.Navigator>
[
and this gives me the following output:](https://i.stack.imgur.com/73o8f.png)
In the main function you see here, the bottomtabnavigator, which I use in the whole app, returns.:
const Main = () => {
return (
<Tab.Navigator screenOptions={screenOptions}>
<Tab.Screen
component={HomeStack}
name="Ana Sayfa"
options={{
tabBarIcon: ({focused, color}) => (
<Ionicons name="home-outline" size={28} color={color} />
),
}}
/>
<Tab.Screen
component={CategoriesStack}
name="Kategoriler"
options={{
tabBarIcon: ({focused, color}) => (
<Ionicons name="grid-outline" size={28} color={color} />
),
}}
/>
<Tab.Screen
component={CartStack}
name="Sepet"
options={{
tabBarIcon: ({focused, color}) => (
<Ionicons name="cart-outline" size={28} color={color} />
),
}}
/>
<Tab.Screen
component={DiscoverStack}
name="Keşfet"
options={{
tabBarIcon: ({focused, color}) => (
<Ionicons name="compass-outline" size={28} color={color} />
),
}}
/>
<Tab.Screen
component={ProfileStack}
name="Profilim"
options={{
headerShown: true,
tabBarIcon: ({focused, color}) => (
<Ionicons name="person-circle-outline" size={28} color={color} />
),
}}
/>
</Tab.Navigator>
);
};
What I want is this; Turning off the header of the drawer in the profile stack in the bottom tab navigator, but I can't do it
but it still didn't close the header of the drawer navigation. Even if I set the headershown of the drawer to false, the header on all screens closes, I'm just not on the screen I want
As far as I understand, there are 3 nested navigator.
Drawer
|---------> BottomTab
| |---------> ProfileStack
|... |...
If you want to hide or show only specific screen you can use navigation.setOptions()
For example,we have a Profile screen in ProfileStack and get navigation with screen props or useNavigation()
If you want to hide ProfileStack header in Profile:
React.useLayoutEffect(() => {
navigation.setOptions({ headerShown:false })
})
If you want to hide BottomTab header in Profile:
React.useLayoutEffect(() => {
navigation.getParent().setOptions({ headerShown:false })
})
If you want to hide Drawer header in Profile:
React.useLayoutEffect(() => {
navigation.getParent().getParent().setOptions({ headerShown:false })
})
You can use setOptions with headerShown:true when the drawer screenOptions headerShown:false
react-navigation getParent() and setOptions() docs

ReactNative: Using Drawer.Navigator inside TabNavigator throws "navigation.openDrawer is not a function"

What I want to achive:
Tap on an icon in bottomTabNavigator to open DrawerNavigator.
What I get:
An error stating: "TypeError: navigation.openDrawer is not a function. (In 'navigation.openDrawer()', 'navigation.openDrawer' is undefined)"
My Code:
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
tabBarOptions={{
activeTintColor: '#e91e63',
style: {},
labelStyle: {margin:0},
}}
>
<Tab.Screen
name="Home"
component={HomeStack}
options={{
tabBarLabel: 'News',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="newspaper" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Categories"
component={CategorieStack}
options={{
tabBarLabel: 'Kategorien',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="format-list-bulleted-square" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Menu"
component={DrawerStack}
options={{
tabBarLabel: 'Menu',
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="menu" color={color} size={size} />
),
}}
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault();
navigation.openDrawer();
}
})}
/>
</Tab.Navigator>
</NavigationContainer>
and
const Drawer = createDrawerNavigator();
function DrawerStack() {
return (
<Drawer.Navigator>
<Drawer.Screen name="Home" component={Home} />
<Drawer.Screen name="Categories" component={Categories} />
</Drawer.Navigator>
);
}
Already read:
https://github.com/react-navigation/hooks/issues/36
Opening the Drawer navigator from the bottom tab
And many more. Just can't seem to get it to work. I think I do miss a really important point in this. Is anyone able to point me into the right direction?
In my case, my bottomTabNavigator is a child of my drawerNavigator( not sure if this is required for it to work tho)
so using this bit works:
listeners={({ navigation }) => ({
tabPress: e => {
e.preventDefault();
navigation.openDrawer();
}
})}
Inside Drawer Navigator:
<Drawer.Navigator
initialRouteName="MyRoute"
drawerStyle={styles.drawerStyle}
lazy
>
{/*Bottom Tab Navigator*/}
<Drawer.Screen
name="MyRoute"
component={MyBottomTabs}
options={{
drawerIcon: ({ focused, size }) => (
<Ionicons
name={focused?"md-home": "md-home-outline"}
size={size}
color={focused ? COLORS.primary : '#ccc'} />
)
}}
/>
<Drawer.Screen
name="Profile"
component={UserProfile}
options={{
drawerIcon: ({ focused, size }) => (
<Ionicons
name={focused?"md-person-sharp": "md-person-outline"}
size={size}
color={focused ? COLORS.primary : '#ccc'} />
)
}}
/>
<Drawer.Screen
name="Elect"
component={WebViewElect}
options={{
drawerIcon: ({ focused, size }) => (
<Entypo
name="new"
size={size}
color={COLORS.danger} />
),
}}
/>
</Drawer.Navigator>
Inside MyBottomTabs:
<Tabs.Navigator initialRouteName="Dashboard" >
{/*Bottom Tab Screens */}
<Tabs.Screen name="Dashboard" component={DashboardStack}/>
<Tabs.Screen name="Refer A Friend" component={WebViewRefer}
options={{
tabBarButton: () => null,
tabBarVisible: false
}}
listeners={({ navigation }) => ({
tabPress: e => {
console.log('Button Pressed')
e.preventDefault();
return navigation.openDrawer();
}
})}
/>
</Tabs.Navigator>

Always show BottomTabNavigation

How can I show BottomTabNavigation even on stacked screen? I have tried this for a few hours but really don't get it to work as expected.
So the thing I want to happen is, if I navigate to say for example the Title Screen, I still want to show the BottomTabNavigation. Any suggestions?
I can of course create a new navigation, but then it is sliding in from the side.
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const HomeTabNavigator = () => {
return (
<Tab.Navigator
tabBarOptions={{
labelStyle: {textTransform: 'uppercase'},
style: {
backgroundColor: '#111111', //Färger på footerbar
borderTopColor: 'transparent',
borderBottomColor: 'transparent',
},
}}>
<Tab.Screen
name={'Concerts'}
component={ConcertsScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image
source={require('../../assets/icons/concerts.png')}
size={25}
/>
),
}}
/>
<Tab.Screen
name={'Docs'}
component={DocumentiesScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/docs.png')} size={25} />
),
}}
/>
<Tab.Screen
name={'Profile'}
component={ProfileScreen}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/user.png')} size={25} />
),
}}
/>
</Tab.Navigator>
);
};
const Router = () => {
const {token, setToken} = useContext(TokenContext);
const {userFav, addFav, getFav} = useContext(UserContext);
const [isLoading, setLoading] = useState(true);
useEffect(() => {
setLoading(false);
setTimeout(() => {}, 1000);
}, []);
return (
<NavigationContainer>
{token ? (
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerTransparent: true,
noBorder: true,
}}
headerMode="float">
<Stack.Screen name={' '} component={HomeTabNavigator} />
<Stack.Screen name={'Concerts'} component={ConcertsScreen} />
<Stack.Screen name={'User Profile'} component={ProfileScreen} />
<Stack.Screen
name={'FavouritesScreen'}
component={FavouritesScreen}
/>
<Stack.Screen name={'Docs'} component={DocumentiesScreen} />
<Stack.Screen name={'AccountScreen'} component={AccountScreen} />
<Stack.Screen name={'Home of'} component={SearchScreen} />
<Stack.Screen name={'Artist'} component={ArtistScreen} />
<Stack.Screen name={'Title'} component={Videos} />
<Stack.Screen name={'PlayVideo'} component={PlayVideo} />
</Stack.Navigator>
) : (
<LoginScreen />
)}
</NavigationContainer>
);
};
You need to nest all your stack screens inside a tab screen.
The BottomTabNavigator disappear because you leave your Tab.Navigator component.
I hope this helps. If you want to navigate between screens that are related to a specific tab button, and have that tab button remain active while moving between these screens, you should set up a StackNavigation within that tab's component. By doing so, the tab button will remain active while navigating within its related screens.
On the other hand, if you want the TabNavigation to be visible throughout the whole application but some screens should not be displayed as tabs, you can add all screens inside the TabNavigation and specify in the options for those screens not to be displayed as tab buttons. That way, while in the screen without a tab button, the tabs will still be visible but none will be active. For example, you can do this for a screen called 'Title':
<Tab.Screen
name={'Title'}
component={Videos}
options={{
tabBarIcon: ({tintColor}) => (
<Image source={require('../../assets/icons/user.png')} size={25} />
),
tabBarButton: () => null <---- *this causes it to have no button*
}}
/>
I hope this helps!

React has detected a change in the order of Hooks called by StackNavigator

I'm using StackNavigator, inside TabNavigator, inside Drawer Navigator.
Trying to redirect to nested Stacks but returns error.
Warning: React has detected a change in the order of Hooks called by StackNavigator. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks:
TypeError: undefined is not an object (evaluating 'prevDeps.length')
What's wront with that?
Why it doesnt redirect to stack.screen below main screen?
my redirect:
<TouchableOpacity style={styles.accessButton} onPress={() => navigation.navigate('Home', {
screen: 'Events'
})}>
My main.js (navigator)
return (
<NavigationContainer theme={NavigationTheme} ref={navigationRef}>
<StatusBar barStyle={darkMode ? 'light-content' : 'dark-content'} />
<Drawer.Navigator
drawerStyle={{ width: '100%' }}
drawerContent={(props) => <DrawerContent {...props} />}
>
<Drawer.Screen name="Startup" component={IndexStartupContainer} />
{isApplicationLoaded && BeforeLogin != null && (
<Drawer.Screen name="Main" component={BeforeLogin} />
)}
</Drawer.Navigator>
</NavigationContainer>
)
my tabbar navigator:
return (
<Tab.Navigator tabBarOptions={{showLabel: false}} lazy>
<Tab.Screen name="Home" component={HomeNavigator}
options={{
tabBarIcon: ({size,focused,color}) => {
return (
<AntDesign name="home" size={39} color={colors.text} />
);
},
}}
/>
<Tab.Screen name="SearchNavigator" component={SearchNavigator}
options={{
tabBarIcon: ({size,focused,color}) => {
return (
<AntDesign name="search1" size={39} color={colors.text} />
);
},
}}
/>
<Tab.Screen name="FAQNavigator" component={FAQNavigator}
options={{
tabBarIcon: ({size,focused,color}) => {
return (
<AntDesign name="questioncircleo" size={39} color={colors.text} />
);
},
}}
/>
<Tab.Screen name="ContactNavigator" component={ContactNavigator}
options={{
tabBarIcon: ({size,focused,color}) => {
return (
<Feather name="phone" size={39} color={colors.text} />
);
},
}}
/>
</Tab.Navigator>
)
and my HomeNavigator:
const HomeNavigator = ({ navigation }) => {
const Stack = createStackNavigator()
const { t } = useTranslation()
return (
<Stack.Navigator>
<Stack.Screen
options={headerStyle_1}
name='Index'
title={t('title.homepage')}
component={HomepageController}
/>
<Stack.Screen
options={headerStyle_1}
name='Events'
title={t('title.events')}
component={EventsController}
/>
</Stack.Navigator>
)
}

React Native how to programmatically navigate to a stack screen that is on a tab navigator

I have searched tirelessly for an answer to this issue.
I have a Tab Navigator:
const AppNavigator = () => {
return (
<Tab.Navigator initialRouteName="Search">
<Tab.Screen
name="Home"
children={() => <HomeScreen />}
options={{
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Search"
children={() => <SearchContainerNavigator />}
options={{
tabBarIcon: ({ color, size }) => (
<FontAwesome5 name="search" color={color} size={size} />
),
}}
/>
<Tab.Screen
name="Feedback"
children={() => <FeedbackScreen />}
options={{
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="email" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
);
};
And this Stack navigator:
const SearchContainerNavigator = () => (
<Stack.Navigator screenOptions={{ headerShown: false}} initialRouteName="SearchSelect">
<Stack.Screen name="SearchSelect" component={SearchSelectScreen} />
<Stack.Screen name="AircraftSearch" component={AircraftSearchScreen} />
<Stack.Screen name="AircraftResults" component={AircraftResultsScreen} />
<Stack.Screen name="AircraftDetail" component={AircraftDetailScreen} />
<Stack.Screen name="BrandSearch" component={BrandSearchScreen} />
<Stack.Screen name="BrandResults" component={BrandResultsScreen} />
<Stack.Screen name="BrandDetail" component={BrandDetailScreen} />
<Stack.Screen name="Favourites" component={FavouritesScreen} />
</Stack.Navigator>
)
My issue is how do I programmatically navigate from HomeScreen tab to the AircraftDetailsScreen on the Search Tab??
Thanks in advance.
EDIT:
HomeScreen is a class.
handleAircraftSelect = (aircraft_id) => {
this.props.navigation.navigate("Search", {
screen: "AircraftDetail",
params: { id: aircraft_id },
});
};
navigation.navigate('Search', {screen: 'AircraftDetail'});
https://reactnavigation.org/docs/nesting-navigators/#navigating-to-a-screen-in-a-nested-navigator
For react-navigation 6.x you can use this like below:
navigation.jumpTo('Profile', { name: 'Michaś' });