Dynamic Title change on the screen - react-native

I have a navigation code:
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name='Home' component={Home} options={{ headerShown: false }} />
<Stack.Screen name='Learning' component={Learning}
options={{
headerTransparent: true,
title: 'Learning',
headerTitleAlign: 'center',
}} />
</Stack.Navigator>
</NavigationContainer>
I use the Learning screen for two functional application screens:
A
B
On screen A, I have two buttons:
<TouchableOpacity onPress={() => navigation.navigate('Learning', { screen: 'Learning' })}/>
<TouchableOpacity onPress={() => navigation.navigate('Learning', { screen: 'Repeats' })}/>
Depending on this content, {screen: 'Learning'} is loading some other data
This screen has a title header
How do you make it load a different title depending on the button you click?
I can't find a solution how to do it for Components built on functions and Hooks

The options prop can take a function and provide {route, navigation} object as an argument for you, so to use the params in the header, you can do this:
<Stack.Screen
name="Learning"
component={Learning}
options={({ route }) => ({ title: route.params.screen })}
/>
I'm not sure what difference the functional components and hooks make in this case, but in case I misunderstood the question, you can use useNavigation and useRoute hooks in your functional components and do the same things that you do in classes.

Related

My react-navigation fullscreeen modal does not cover the tabs navigation on android

I'm currently struggeling with a behavior on react-navigation for react-native. My Navigation has a bottom tab navigator which has four entries represented as Stacks.
const Tabs = createBottomTabNavigator<AppParamList>();
export const AppTabs: React.FC<AppTabsProps> = () => {
return (
<Tabs.Navigator>
<Tabs.Screen
name="Store"
options={{
title: i18n.t("STORE.STORE"),
header: () => null,
}}
component={StoreStack}
/>
<Tabs.Screen
name="Favorites"
options={{ title: i18n.t("FAVORITES"), header: () => null }}
component={FavoriteStack}
/>
<Tabs.Screen
name="Orders"
options={{ title: i18n.t("ORDERS"), header: () => null }}
component={OrdersStack}
/>
<Tabs.Screen
name="Account"
options={{ title: i18n.t("ACCOUNT"), header: () => null }}
component={AccountStack}
/>
</Tabs.Navigator>
);
};
One of those Stacks, the first one, includes a Stack which is represented as a fullscreen modal ( The last one called checkout).
const Stack = createNativeStackNavigator<StoreParamList>();
export const StoreStack: React.FC<StoreStackProps> = () => {
return (
<Stack.Navigator initialRouteName="Home">
<Stack.Screen
options={{ header: () => null }}
name="Home"
component={HomeScreen}
/>
<Stack.Screen
options={{ header: () => null }}
name="Product"
component={ProductScreen}
/>
<Stack.Screen
options={{ header: () => null, presentation: "fullScreenModal" }}
name="Checkout"
component={CheckoutStack}
/>
</Stack.Navigator>
);
};
Now this does work as intended on ios, where its just opening a fullscreen modal only showing my checkout stack. My problem is, that, since the modal stack is contained within my TabsNavigator, on android it opens the modal while still presenting the TabNavigator on the bottom. Does anyone have a advice on how I can go around this problem?
There is a flag on the tabsNavigator, where you can remove the tabBar
options={{
tabBarStyle: { display: "none" },
}}
, however that is only on the upper level and is not available within my Tabs.Screens. I was thinking of moving up the modal to that level, but then it displays a fifth entry in my tabsBar and also complicates the navigation with Typescript.

Update screen title to that of selected tab navigation

I am in the process of implementing drawer navigation with nested tab navigation within my app.
At present I have two issues that I cannot work out so far and am looking for pointers. I am using React Navigation v6.
Upon clicking any of my tab navigator links I notice that my screen title is always set to "Home". Can this be updated to the current selected tab?
The last clicked tab appears to keep its state when using the drawer links, the journey would look like
Start on home page (Renders home screen fine)
click userprofile tab (Renders user profile page fine)
then click "Book New Class" from within the Drawer, (Renders New Class page fine)
then click "Home" from within the Drawer (This renders the user profile screen)
I have created a snack that will hopefully show the issues I'm facing https://snack.expo.dev/#richlewis14/drawer-and-tab-navigation
DrawerNavigator.js
import React, {useContext} from 'react';
import CustomDrawer from '../components/CustomDrawer.js';
import {BookNewEvent} from '../screens/events/BookNewEvent.js';
import {UserProfile} from '../screens/profile/UserProfile.js';
import BottomTabNavigator from '../navigation/BottomTabNavigator.js';
import {createDrawerNavigator} from '#react-navigation/drawer';
import Ionicons from 'react-native-vector-icons/Ionicons';
const Drawer = createDrawerNavigator();
const DrawerNavigator = ({navigation}) => {
return (
<Drawer.Navigator
drawerContent={props => <CustomDrawer {...props} />}
screenOptions={{
drawerActiveBackgroundColor: '#2B6EB5',
drawerActiveTintColor: '#fff',
drawerInactiveTintColor: '#333',
drawerLabelStyle: {marginLeft: -25, fontSize: 15},
}}>
<Drawer.Screen
name="Home"
component={BottomTabNavigator}
options={{
title: 'Home',
drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
}}
/>
<Drawer.Screen
name="BookNewEvent"
component={BookNewEvent}
options={{
title: 'Book Class',
drawerIcon: ({color}) => <Ionicons name="calendar-outline" size={22} color={color} />,
}}
/>
</Drawer.Navigator>
);
};
export default DrawerNavigator;
BottomTabNavigator.js
import React, {useContext} from 'react';
import {UserLandingPage} from '../screens/landing/UserLandingPage.js';
import {BookNewEvent} from '../screens/events/BookNewEvent.js';
import {UserProfile} from '../screens/profile/UserProfile.js';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import {createNativeStackNavigator} from '#react-navigation/native-stack';
import Ionicons from 'react-native-vector-icons/Ionicons';
const Tab = createBottomTabNavigator();
const Stack = createNativeStackNavigator();
const UserHomeStack = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="Home"
component={UserLandingPage}
options={{headerShown: false}}
/>
</Stack.Navigator>
);
};
const BottomTabNavigator = ({navigation}) => {
return (
<Tab.Navigator
screenOptions={{
headerShown: false,
tabBarShowLabel: false,
}}>
<Tab.Screen
name="Home2"
component={UserHomeStack}
options={{
tabBarIcon: () => <Ionicons name="home" size={22} />,
}}
/>
<Tab.Screen
name="UserBookNewEvent"
component={BookNewEvent}
options={{
tabBarIcon: () => <Ionicons name="add-circle" size={22} />,
}}
/>
<Tab.Screen
name="UserProfilePage"
component={UserProfile}
options={{
tabBarIcon: () => <Ionicons name="person" size={22} color="black" />,
}}
/>
</Tab.Navigator>
);
};
export default BottomTabNavigator;
You are nesting a Tab.Navigator inside a Drawer.Navigator. Compare this with the following code snippet from your snack.
<Drawer.Screen
name="Home"
component={BottomTabNavigator}
options={{
title: 'Home',
drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
}}
/>
This is not a problem but you need to keep in mind that nesting navigators change certain behaviors of your application, e.g.
Each navigator has its own options​.
For example, specifying a title option in a screen nested in a child navigator won't affect the title shown in a parent navigator.
We can fix this by using the code provided in the official documentation of react-native for setting parent screen options based on child navigators state.
Here is what I have done in order to fix your title issue. In BottomTabNavigator.js:
const BottomTabNavigator = ({navigation, route}) => {
function getHeaderTitle(route) {
const routeName = getFocusedRouteNameFromRoute(route) ?? 'Home';
switch (routeName) {
case 'Home2':
return 'Home';
case 'UserBookNewEvent':
return 'Book Class';
case 'UserProfilePage':
return 'Profile';
}
}
React.useLayoutEffect(() => {
navigation.setOptions({ headerTitle: getHeaderTitle(route) });
}, [navigation, route]);
...
Notice that our BottomTabNavigator is a child of the Drawer for the Home-Screen, thus it seems that both navigator navigate to the same screen while updating each others navigation states. This is not possible. We can see this, if we navigate via the Drawer to a different screen, then its title is now updated, however, the screen stays the same.
If it is desired to navigate via the Tab.Navigator if the user is on the Home-screen, such that both navigators stay visible, then this is possible. If the user navigates to a screen, different then the Home- screen via the Drawer, then the Tab.Navigator will vanish.
Thanks to the discussion from the comments, this is acceptable. Solving your second issue can be done with a little hack by setting the prop unmountOnBlur to true for the Home-screen provided in the DrawerNavigator as follows.
<Drawer.Screen
name="Home"
component={BottomTabNavigator}
options={{
title: 'Home',
unmountOnBlur:true,
drawerIcon: ({color}) => <Ionicons name="home" size={22} color={color} />,
}}
/>
Please consider my updated snack for a full working solution.

How to navigate to a another screen in class component in react native

App.js code:
function firstScreenStack({ navigation }) {
return (
<Stack.Navigator initialRouteName="Login">
<Stack.Screen
name="Login"
component={Login}
options={{
title: 'Login', //Set Header Title
headerLeft: ()=>
<NavigationDrawerStructure
navigationProps={navigation}
/>,
headerStyle: {
backgroundColor: '#CA2C68', //Set Header color
},
headerTintColor: '#fff', //Set Header text color
headerTitleStyle: {
fontWeight: 'bold', //Set Header text style
},
}}
/>
</Stack.Navigator>
);
}
function secondScreenStack({ navigation }) {
return (
<Stack.Navigator
initialRouteName="Browse"
screenOptions={{
headerLeft: ()=>
<NavigationDrawerStructure
navigationProps={navigation}
/>,
headerStyle: {
backgroundColor: '#CA2C68', //Set Header color
},
headerTintColor: '#fff', //Set Header text color
headerTitleStyle: {
fontWeight: 'bold', //Set Header text style
}
}}>
<Stack.Screen
name="Browse"
component={Browse}
options={{
title: 'Browse', //Set Header Title
}}/>
<Stack.Screen
name="Profile"
component={Profile}
options={{
title: 'Profile', //Set Header Title
}}/>
</Stack.Navigator>
);
}
export default App function has below code:
<NavigationContainer>
<Drawer.Navigator
initialRouteName = {Login}
drawerContentOptions={{
activeTintColor: '#CA2C68',
itemStyle: { marginVertical: 5 },
}}>
<Drawer.Screen
name="Login"
options={{ drawerLabel: 'Login' }}
component={firstScreenStack} />
<Drawer.Screen
name="Browse"
options={{ drawerLabel: 'Browse' }}
component={secondScreenStack} />
<Drawer.Screen
name="Profile"
options={{ drawerLabel: 'Profile' }}
component={profileScreenStack} />
</Drawer.Navigator>
</NavigationContainer>
I am using stack and drawer navigation both.
I want navigation.navigate("Profile");
Now how can I get navigation props into my class components? I am new in react native and navigation. Its bit complicated to understand for me. If you can help me out it will be better. Thanks.
If we are supposed to navigate from the screen of the first stack navigator to another navigator then we have to access the parent of the first navigator's screen, which mean we have to get the navigation prop of the first stack navigator as we have created a drawer based on the multiple screens.
fox ex. if you want to navigate from the Browse screen of firstScreenStack to another drawer screen navigator then have a try with the below code:
if using Class Component:
this.props.navigation.dangerouslyGetParent()?.navigate("<<ANOTHER_DRAWER_SCREEN>>")
if using Functional Component:
props.navigation.dangerouslyGetParent()?.navigate("<<ANOTHER_DRAWER_SCREEN>>")
Navigation is accessible in stack routes only i.e, those classes or components which are used as routes in different navigators.
Now if i got your question, you want to access it in all components irrespective of whether it is a route or not. You can do that by passing props to it from a component which is a route.
E.g,
Let main be a route and child be another component but not a route.
Inside the main;
<child new_navigation={this.props.navigation} />
Now you can access the new_navigation inside the child.
Inside the child;
this.props.new_navigation.navigate('some_route') //you can now use all the other methods also like push, replace etc.

Show the arrowLeft back button with a disabled header

hello community im relatively new to React native I want to show the arrow left to go back to screens !! when i run my code it doesnt show up , and i think i have to disable my headershown:flase and i put it on commentary ! but unfortunately i have this error Objects are not valid as a React child (found:Object with keys{left}) This is my code :
App.js
<Stack.Navigator
initialRouteName="Home"
>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerShown:false
}}
/>
<Stack.Screen
name="Sign In"
component={SignIn}
options={{
headerShown:false,
header: ({ goBack }) => ({
left: ( <Icon name={'arrowleft'} onPress={ () => { goBack() } } /> ),
})
}}
/>
<Stack.Screen
name="Registration"
component={Registration}
options={{
headerShown:false,
header: ({ goBack }) => ({
left: ( <Icon name={'arrowleft'} onPress={ () => { goBack() } } /> ),
})
}}
/>
</Stack.Navigator>
</NavigationContainer>
Try modifying your options like so:
options={{
headerTransparent: true,
headerBackImage: () => <Icon name={'arrowLeft'} />,
}}
This will change only the back icon (you might add some styling to position it as you want), and ReactNavigation will handle press events.
I don't think you want to use headerShown: false as this will hide the entire navigation bar, back button included.
The error you are receiving is due to the fact that the API changed a few versions back and the header property now requires a component to render, not an object.

Transition in the application with navigation hooks

I have an application that uses Hooks
My navigation:
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name='Home' component={Home} options={{ headerShown: false }} />
<Stack.Screen name='Learning' component={Learning}
options={({ route }) => ({
headerTransparent: true,
title: route.params.screen
})} />
</Stack.Navigator>
</NavigationContainer>
The standard transition is the outgoing screen from the bottom of the screen
I would like to make the screen disappear and a second one appear (fadeIn, fadeOut)
In class-based applications, I can use this library to change the style of transition between screens:
(it doesn't work on hooks)
react-navigation-transitions