React native navigation button in the header with parameters - reactjs

There is a button in the header that is suppose to go to detail page and send parameters. The issue I am having is that when the button is clicked nothing happens. Could you help with this.
I am not sure how to insert navigation button in the header and pass parameters to a screen
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{
headerRight: (navigation) => (
<Button
title="Go to Details"
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
),
}}
/>
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}

You have to pass the navigation param to the options prop of the screen.
Try something like this:
<Stack.Screen
name="Home"
component={HomeScreen}
options={({navigation}) => ({
headerRight: () => (
<Button
title="Go to Details"
onPress={() => {
/* 1. Navigate to the Details route with params */
navigation.navigate('Details', {
itemId: 86,
otherParam: 'anything you want here',
});
}}
/>
),
})
/>

Related

react navigation how to pass navigation object to tabBarButton?

I am using create Bottom Tab Navigator , version 6 , is there a way to pass navigation object from Tab.Screen to TabButton ? taking in account props of tabBarButton that I must also pass them to my custom component (TabButton) , here is my code thanks for your help :
<Tab.Screen key={index} name={item.route} component={item.component}
options={{
headerShown: false,
tabBarButton: (props) => <TabButton {...props} item={item} />
}}
/>
you can pass navigation and tabBarButton props in this way,
The options function receives the navigation prop and the route prop for that screen, so you may also use route there.
<Tab.Screen key={index} name={item.route} component={item.component}
options={({ navigation }) => ({
headerShown: false,
tabBarButton: (props) => <TabButton {...props} navigation={navigation} item={item} />
})
}
/>
Here's the example how you can do it
<Stack.Screen
name={"ComponentName"}
component={ComponentScreen}
options={(props) => {
return {
header: () => (
<CustomHeaderComponent
title={"SomeCoolTitle"}
{...props}
/>
),
};
}}
/>
I just added a global variable and affect to it props.navigation , like so :
return (
<Tab.Screen key={index} name={item.route} component={item.component}
options={( props ) => {
_navigation = props.navigation
return{
tabBarButton: (props) => <TabButton {...props} item={item} navigation={_navigation} />
}
}
}
/>
)

TypeError: undefined is not an object (evaluating 'navigation.replace') in react native

I am using bottom tab navigator for my app in which I have used stack navigation for each tab. I have one notification icon on header on click of that I navigate to notification page. The notification page has that global header too in which on click of app logo it will go back to home page.
The header code works fine everywhere but when I click on logo on notification page it throws error of "TypeError: undefined is not an object (evaluating 'navigation.replace')"
And I find on logo.js file when I am on notification page the navigation object is blank otherwise any other screen I am getting notification object.
I have used same code for all the files but getting error only on this screen. Please help.
<BottomTab.Screen
name="Notifications"
component={TabSixNavigator}
options={{
tabBarButton: () => (
<View style={{ width: 0, height: 0 }}></View>
),
}}
/>
//Stack for Notification
const TabSixStack = createStackNavigator<TabSixParamList>();
function TabSixNavigator() {
return (
<TabSixStack.Navigator
screenOptions={({navigation}) => ({
headerStyle: { backgroundColor: Colors.light.topBarColor },
headerRight: () => (
<GlobalHeader
navigation={navigation} />
),
headerLeft: () => (
<GlobalLogo navigation={navigation} />
),
})}
>
<TabSixStack.Screen
name="Home"
component={Home}
options={({navigation}) => ({
title: null,
headerShown: true,
headerBackTitleVisible: false,
headerTitleAlign: "center",
headerLeft: () => (
<GlobalLogo navigation={navigation} />
),
})}
/>
<TabSixStack.Screen
name="Notifications"
component={Notifications}
options={({navigation})=> ({
title: null,
headerShown: true,
headerBackTitleVisible: false,
headerTitleAlign: "center",
})}
/>
</TabSixStack.Navigator>
);
}
//GLobalLogo.js
export default function GlobalLogo({ navigation }) {
<TouchableOpacity onPress={() => navigation.replace("Home")}>
<Image source={require("../assets/images/fence.png")} alt="" />
</TouchableOpacity>
}
you will not receive navigation object from headerLeft or any other header configuration of react-navigation.
There are 2 ways u can achieve this:
first one is to write in line component like this:
headerTitle: () => <Text>Register</Text>,
headerRight: () => <Button title="Test" onPress={() => navigation.navigate('Home')} />,
headerLeft: () => <Button title="back home" onPress={() => navigation.goBack()} />,
the second one is to use useNavigation hook of react-navigation if you have to make a separate header component. Also, this hook is introduced in React Navigation 5x.

React Naitve Navigation: How to show a modal from drawer?

Is there a way to show a modal from drawer in React Native?
There was a similar question and I changed my code following the answer.
My current code is like this.
MyRootStackNavigator.tsx
const Stack = createStackNavigator<RootStackParamList>();
const MyRootStackNavigator = () => {
return (
<Stack.Navigator mode="modal">
<Stack.Screen
name="Main"
component={MyDrawerNavigator}
options={{ headerShown: false }}
/>
<Stack.Screen
name="MyModal"
component={MyModal}
/>
</Stack.Navigator>
);
};
MyDrawerNavigation.tsx
const Drawer = createDrawerNavigator();
const MyDrawerNavigator = () => {
return (
<Drawer.Navigator>
<Drawer.Screen
name="Home"
component={MyStackNavigator}
/>
<Drawer.Screen
name="About this app"
component={About}
/>
</Drawer.Navigator>
);
}
But for this code the section to show a modal doesn't appear on drawer. Only Home and About this app sections appear on drawer. How can I set the section to show a modal on drawer?
You can use a CustomDrawerContent which has a modal inside and use a button to open the Modal
function CustomDrawerContent(props) {
const [modalVisible, setModalVisible] = useState(false);
return (
<DrawerContentScrollView {...props}>
<Modal
style={{ flxe: 1, backgroundColor: 'red' }}
visible={modalVisible}
onRequestClose={() => setModalVisible(false)}>
<Text>Modal Content Here</Text>
<Button title="Close" onPress={() => setModalVisible(false)} />
</Modal>
<DrawerItemList {...props} />
<DrawerItem
label="Open Modal"
onPress={() => {
setModalVisible(true);
}}
/>
</DrawerContentScrollView>
);
}
And the DrawerNavigator should be setup like this
<Drawer.Navigator
drawerContent={(props) => <CustomDrawerContent {...props} />}>
You can refer the docs here
https://reactnavigation.org/docs/drawer-navigator/#providing-a-custom-drawercontent
If you want to continue with the code that you have put in the question, you can do like below to navigate to the about screen (Instead of having the modal inside the content)
<DrawerItem
label="Open Screen"
onPress={() => {
props.navigation.navigate('About')
}}
/>

Why my does drawer navigator screen has no header title, using nested (StackNavigator, DrawerNavigator)

I have problem regarding disappeared header title, Currently I used Nested StackNavigator and DrawerNavigator, I have module when the user is logged in they use the drawer navigator else they will back to stack navigator.
Problem: Why does my header title disappeared ?
Here is my App.js
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
const UnauthenticatedScreen = () => {
return (
<Stack.Navigator>
<Stack.Screen
name="Login"
component={Login}
options=
{{
headerShown: false,
}}
/>
<Stack.Screen
name="Registration"
component={Register}
options={{
headerStyle: {
backgroundColor: '#4ABDFF'
},
headerTitleStyle: {
color: '#fff',
},
headerTintColor:'#fff',
}}
/>
<Stack.Screen
name="Privacy"
component={PrivacyPolicy}
options={{
headerStyle: {
backgroundColor: '#4ABDFF'
},
headerTitleStyle: {
color: '#fff',
},
headerTitle:'Privacy Policy',
headerTintColor:'#fff',
}}
/>
<Stack.Screen
name="RegistrationSuccess"
component={RegistrationSuccess}
options=
{{
headerShown: false,
}}
/>
</Stack.Navigator>
)
}
const AuthenticatedDriverScreen = () => {
return (
<Drawer.Navigator drawerContent={props=><CustomDriverDrawer {...props} />} initialRouteName="DriverDashboard">
<Drawer.Screen
name="DriverDashboard"
component={DriverDashboard}
options={
{
drawerLabel: 'Home',
drawerIcon: ({focused, color, size}) => (
<Icon type="font-awesome" name="home" style={{fontSize:size, color:"black"}} />
),
}
}
/>
</Drawer.Navigator>
)
}
function Navigator() {
const isLogin = true;
return (
<NavigationContainer>
{isLogin ? <AuthenticatedDriverScreen/> : <UnauthenticatedScreen/>}
</NavigationContainer>
)
}
Authenticated User Screen:
Very well appreciated for your help.
First of all, you need to remember that thing React Navigation providing drawer functionality and screen functionality separately.
for connecting two functionality in one you need to bind the screen with a drawer so you can see both things in one go.
that's why first you need to make one screen navigator that contains single/multiple screen(s) so you can bind that navigator with a drawer like the below code.
const BindedScreenComponent = () => {
return (
<Bind.Navigator>
<Bind.Screen
name="Bind"
component={() => <Text>Bind</Text>}
/>
</Bind.Navigator>
)
}
after that, you need to bind that code into the drawer component so the drawer component can render a whole navigator and give you the desire results
<Drawer.Screen
name="DriverDashboard"
component={BindedScreenComponent}
options={
{
drawerLabel: 'Home',
}
}
/>
snake: https://snack.expo.io/#nishargshah/react-navigation-5-boilerplate
My Old Explanation is correct for multiple screens bound in one screen but if you want only one screen with one drawer then you can use headerShown: true in your drawer options so, it will automatically show the header of the drawer.
Here is the example: https://snack.expo.io/#nishargshah/react-navigation-5-drawer

Why route object sometimes have state and sometimes don't - in react-navigation v5

Start:
<NavigationContainer>
<Stack.Navigator>
{
isLoading ? <Stack.Screen name="AuthLoadingScreen" component={AuthLoadingScreen} />
: (
user ? (
<Stack.Screen name="AuthenticatedStack" component={AuthenticatedStack} options={{headerShown: false}} />
) : (
<Stack.Screen name="NotAuthenticatedStack" component={NotAuthenticatedStack} options={{headerShown: false}} />
)
)
}
</Stack.Navigator>
</NavigationContainer>
Authenticated stack:
const AuthenticatedStack = () => {
return (
<Drawer.Navigator initialRouteName="MainStack" drawerContent={props => <SideBar {...props} />}>
<Stack.Screen name="MainStack" component={MainStack} options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} />
</Drawer.Navigator>
);
};
Main stack contain main screen with tab navigator and other navigators which I plan to navigate to from side menu buttons:
const MainStack = () => {
return (
<Stack.Navigator>
<Stack.Screen name="main" component={MainTabNavigator} options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} />
<Stack.Screen options={({route}) => ({
title: getHeaderTitle(route),
})} name="Welcome" component={WelcomeStack} />
</Stack.Navigator>
);
};
Main tab navigator is just few tabs:
const MainTabNavigator = () => {
return (
<Tab.Navigator>
<Tab.Screen
...
Home stack navigator:
const HomeStackNavigator = ({navigation, routes}) => {
return (
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} options={{
headerLeft: () => (),
}} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
);
};
Welcome stack:
const WelcomeStack = () => {
return (
<Stack.Navigator>
<Stack.Screen options={({route}) => ({
headerShown: shouldHeaderBeShown(route),title: getHeaderTitle(route),
})} name="welcome" component={WelcomeScreen} />
</Stack.Navigator>
);
};
I use this two function to show hide header to avoud double headers on tabs and to set titles:
function shouldHeaderBeShown(route) {
const routeName = route.state ? route.state.routes[route.state.index].name : 'Home';
switch (routeName) {
case 'Home': return false;
}
}
function getHeaderTitle(route) {
const routeName = route.state ? route.state.routes[route.state.index].name : 'Home';
switch (routeName) {
case 'Home': return 'Home';
case 'Settings': return 'Settings';
case 'Welcome': return 'Welcome';
}
}
And here I got to the problem:
This line: route.state ? route.state.routes[route.state.index].name if I switch between tabs I get all correctly state property is there and it's fine titles are set.
But If I from drawer navigate to Welcome then route doesn't have state property so it always pick 'Home' for title.
I pass this by making this line to:
const routeName = route.state ? route.state.routes[route.state.index].name : route.name;
Then it is showing Welcome title.
But I don't understand is this workaround ok or this screens can be set in a better way?
But If I from drawer navigate to Welcome
How do you navigate from drawer? If you're navigating by passing screen param, you can do this:
function getActiveRouteName(route) {
// Access the tab navigator's state using `route.state`
const routeName = route.state
? // Get the currently active route name in the tab navigator
route.state.routes[route.state.index].name
: // If state doesn't exist, we need to default to `screen` param if available, or the initial screen
// In our case, it's "Home" as that's the first screen inside the navigator
route.params?.screen || 'Home';
return routeName;
}
Also seems the reason you even need this is because you have so many nested navigators. Maybe try to reduce nesting first? https://reactnavigation.org/docs/en/nesting-navigators.html#best-practices-when-nesting
I think you should use dispath not navigate.
In navigate, you can't get updated state if you navigate to that screen again.
import {StackActions} from 'react-navigation';
pushAction = StackActions.push({
routeName: 'FormsScreen',
params: {
id: result.id,
flag: true,
comments: result.note,
type: result.type,
date: result.created,
accountName: result.accountName,
accountId: result.accountId,
accountType: result.accountType,
bundleId: result.bundleId,
formAnswer: JSON.parse(result.formAnswer),
},
this.props.navigation.dispatch(pushAction);

Resources