How to set NavigationOptions of a parent? - reactjs

I have nested navigator in my app. I would like my header bar to be set on the root of the navigaton, so there will be a mutual header bar to all screens in my application.
my index.js is:
const StackOpportunityNavigator = createStackNavigator(
{
MainOpportunity: OpportunityScreen,
ClientScreen: ClientScreen,
BusinessMapLocation: BusinessMapLocation,
LoginScreen: LoginScreen
},
{
initialRouteName: 'MainOpportunity',
headerMode: 'none',
transitionConfig: () => fromLeft(600),
}
);
const DrawerNavigationOptions = createDrawerNavigator(
{
OpportunityStack: { screen: StackOpportunityNavigator },
History: HistoryScreen,
MyChances: MyChancesScreen,
Info: InfoScreen
},
{
contentComponent: (props) => <SideBar {...props} />,
drawerPosition: 'right',
transitionConfig: () => fromLeft(600),
}
);
const LoginNavigator = createStackNavigator(
{
LoadingScreen: LoadingScreen,
LoginScreen: LoginScreen,
DrawerNavigator: DrawerNavigationOptions
},
{
transitionConfig: () => fromLeft(600),
headerMode: 'screen',
navigationOptions: {
headerStyle: {
backgroundColor: Colors.primary
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'normal',
fontSize: 18
}
}
}
);
export default LoginNavigator;
In every screen in my application i have this code:
static navigationOptions = {
header: <HeaderBar title={'currentScreenTitle'} />
};
However it works from 'LoginScreen' screen and all the others from 'LoginNavigator', but not from any other screen like 'History'.
How can i still control the navigation options from other screen like 'History' and set the title, if my navigation header bar is in the most root navigator?
Hope you understand my question.
Really hope to for your answers as fast as possible.
Thanks guys!

From what I understand there is no way to access the parent navigator from within a nested screen.
The documentation states: "You can only modify navigation options for a navigator from one of its screen components. This applies equally to navigators that are nested as screens."
It's not very convenient but I think creating your own component is the only way to achieve this. There is a helpful library that will make creating your header component easier though. React Native Elements

I have been having the same issue what I did is made header null in navigationOptions and added my header component in screen as a regular component and performed all the logic, Its not the best practice but a work around :)

Related

How to implement react-navigation stack/bottomTab navigator using back buttons instead of tabs to navigate?

I have implemented a react native application that currently does not use any type of navigation. I want to implement react-navigation but am struggling to recreate what I already have. Below I have added some images below to give an example of what I currently have without react-navigation. I would like to replicate this exactly using react-navigation.
This is the home screen: https://ibb.co/XWxCpwt
This it the right tab view (the left tab view button changes to a back button): https://ibb.co/XzFB8v8
This is the left tab view (the right tab view button changes to a back button): https://ibb.co/zP2ZBK5
I want to make it clear that the center bottom button has nothing to do with showing the center view. It has a completely different functionality. And that is the reason why I would like the back buttons to work this way.
This is a little snippet from my App.js file. Without react-navigation everything above the exported class would be commented out. I have not spent much time trying to figure this out as I already did not have much luck trying to implement with react-native-navigation. Any help would be appreciated!
const TabNavigator = createBottomTabNavigator({
Community: Community,
Root: Root,
Network: Network
});
const RootStack = createStackNavigator({
Root: Root
});
const Navigation = createAppContainer(TabNavigator);
// Everything above this line would normally be commented out and <Root /> would
// be inside the exported class instead of <Navigation />
export default class App extends Component {
render() {
return (
<Provider store={persistStore.store} >
<PersistGate loading={null} persistor={persistStore.persistor}>
<Navigation />
</PersistGate>
</Provider>
);
}
}
I have updated my App.js. This is the closest I was able to get thus far. The next steps needed are to configure the tabs to not show the view currently linked to the tab upon click, but to instead change to back buttons to return a user to the home screen (the Root component). The desired navigation look of this app is to feel like there are three views sitting side by side. The user can only navigate one view at a time and cannot skip between.
const TabNavigator = createBottomTabNavigator({
Community: Community,
Home: Root,
Network: Network
});
const RootStack = createStackNavigator({
Root: {
screen: TabNavigator,
navigationOptions: {
headerLeft: () => <ProfileSidebarButton />,
headerTitle: () => <Search />,
headerRight: () => <MapFilterButton />
}
}
});
const Navigation = createAppContainer(RootStack);
I was able to solve my configuration problem with this solution. The key was to use the tabBarComponent to render my custom tab bar. I then passed the navigation prop to the buttons. I then implemented my own navigation logic using this.props.nav.navigate('Community') (in my case I passed the navigation prop as 'nav'). This seems to be working well atm, there is a small issue with the Rootstack rendering slightly up and sliding down each time it is navigated to.
const HomeStack = createDrawerNavigator({
Root: Root,
Profile: Profile
},{
initialRouteName: 'Root',
drawerWidth: SCREEN_WIDTH,
contentComponent: props => (
<ProfileSidebar drawerNavigator={props.navigation}/>
)
});
const TabNavigator = createBottomTabNavigator({
Community: {
screen: Community
},
Home: HomeStack,
Network: {
screen: Network
}
},{
initialRouteName: 'Home',
tabBarComponent: props => (
<View style={styles.nocturnFooter}>
<NavButton nav={props.navigation}/>
<NocturnButton />
<CommunityButton nav={props.navigation}/>
</View>
),
});
const RootStack = createStackNavigator({
Root: {
screen: TabNavigator,
navigationOptions: ({ navigation }) => ({
headerLeft: () => <ProfileSidebarButton />,
headerTitle: () => <Search />,
headerRight: () => <MapFilterButton />,
headerTransparent: navigation.state.index == 1 ? true : false,
headerStyle: navigation.state.index != 1 ? {
backgroundColor: '#000000'
} : {}
})
}
});
const Navigation = createAppContainer(RootStack);

React Native Tab Navigation nested in Stack Navigation How To?

I am new to react native, and have come across this issue with navigation. I have a 'main menu' (TabNav) that I want to render on every page.
I have some pages that are not accessible from the main menu (TabNav), but should still have the menu options. I'm not sure how to impliment this. Here is the code I have so far:
const TabNav = createMaterialBottomTabNavigator({
Map: { screen: HomeScreen },
Details: { screen: Details },
});
const extraNav = createStackNavigator(
{
SinglePage: SinglePage,
Tabs: TabNav
}
);
const RootStackNav = createStackNavigator(
{
Tabs: {
screen: TabNav
},
SinglePage: {
screen: extraNav,
}
},
);
I navigate to the single separate page via:
render() {
const { navigation } = this.props;
return (
<View>
<Icon
name='place'
onPress={() => navigation.navigate('SinglePage')}
/>
</View>
);
}
What I want to happen is when I navigate to the single separate page my "main menu" is still present.
Right now I can navigate to all the pages, but on the single separate page the tabs are not available, and I have to hit 'back' to access it again.
You can use this code to nest a stack navigator to a tab navigator
const RootTabNav = createMaterialBottomTabNavigator({
Map: { screen: HomeScreen },
DetailStack: { screen: stackNav},
});
const stackNav= createStackNavigator(
{
SinglePage: { screen: SinglePage}
Details: { screen: Details },
}
);
Its just an example of nesting stack to tabNavigator. I hope this may help you.

Displaying headers with DrawerNavigator in React Native

I'm looking to use DrawerNavigator along with StackNavigator so I can display a header for my screens, everything is working fine but I messed up with the router logic, my issue is that the drawer do not render items but only the first one.
const Router = StackNavigator({
Home: {screen: Screen1},
Other: {screen: Screen2}
}, {
navigationOptions: {
headerStyle: {backgroundColor: '#333333'},
headerTintColor: '#fff'
}
});
const Drawer = DrawerNavigator({
App: {screen: Router},
});
export default Drawer;
Can someone please explain why I'm only seeing the first item in drawer? When trying to fix that I added a second item to drawer config router like that
const Drawer = DrawerNavigator({
App: {screen: Router},
App: {screen: Router},
});
It add a second item to the drawer but with the wrong title, it use title for screen one but the link is correct, it redirect to the second screen. so how can I fix those labels?
Better way of doing this is by creating drawernavigator with custom "contentComponent". This way you will have more control of what titles you want to display in the drawer, on click of title which screen it has to navigate to, background color of the drawernavigator.
So the DrawerNavigator is instantiated like this (from the docs):
const MyApp = DrawerNavigator({
Home: {
screen: MyHomeScreen,
},
Notifications: {
screen: MyNotificationsScreen,
},
});
Therefore by doing what you did the first time:
const Drawer = DrawerNavigator({
App: {screen: Router},
});
, you're telling DrawerNavigator that there is only one element to be added to the drawer menu, regardless of what that screen "Router" contains, for DrawerNavigator it is just one element.
So in order to correctly add your screens to the drawer:
const HomeRoute = StackNavigator({
Home: {screen: Screen1},
}, {
navigationOptions: { ... }
});
const OtherRoute = StackNavigator({
Other: {screen: Screen2},
}, {
navigationOptions: { ... }
});
const Drawer = DrawerNavigator({
Home: { screen: HomeRoute },
Other: { screen: OtherRoute }
});
export default Drawer;

NewCard screen from DrawerNavigator item

I hope i'm able to explain this properly, and that there isn't already an answer out there. It seems there is still a few design decisions up in the air for apps that have a StackNavigator nested inside DrawerNavigator.
What I'm trying to achieve: I have a link to a "Settings" screen in my DrawerNavigator, similar to a lot of apps. I will use Google Play Music as an example for what I want. Clicking on "Settings" sends you to a new screen with only a "back" / "done" button. The Drawer Menu is not accessible.
Question: How can I add a link in the DrawerNavigator that links to a new card/modal view? I'm guessing it can be achieved my some nested navigator stack, but I haven't been able to get anything that works.
Sample code:
const DashboardNavigator = StackNavigator({
Home: { screen: HomeScreen }
})
const SettingsNavigator = StackNavigator({
Settings: {screen: SettingsScreen}
// I thought adding 'mode': 'modal would give me the functionality
// I'm looking for my it doesn't
}, { mode: 'modal', initialRoute: 'Settings' })
const DrawerNavigation = DrawerNavigator({
Home: {
screen: DashboardNavigator
},
Settings: {
screen: SettingsNavigator
}
})
There is a pull request to allow disabling the Drawer Menu on specific screens so i'm not really worried about that right now, but just navigating to a screen where navigation.goBack() takes me back to the last screen I was on (with the card slide animation).
Was able to get it with this, although I still can access the Drawer menu... Hopefully they add the ability to disable the Drawer soon.
const DrawerComponent = ({ navigation }) => {
return (
<View>
<Button onPress={() => navigation.navigate("SettingsView")} title="settings" />
</View>
)
}
const DashboardNavigator = StackNavigator({
Home: { screen: HomeScreen },
SettingsView: { screen: SettingsScreen }
})
const DrawerNavigation = DrawerNavigator({
Home: {
screen: DashboardNavigator
}
}, { contentComponent: DrawerComponent })
const RootNavigator = StackNavigator({
Root: {
screen: DrawerNavigation
}
})

Disable back button in react navigation

I'm using react native navigation (react-navigation) StackNavigator.
it starts from the Login page throughout the whole lifecycle of the app. I don't want to have a back option, returning to the Login screen. Does anyone know how it can be hidden on the screen after the login screen?
BTW, I'm also hiding it in the login screen by using:
const MainStack = StackNavigator({
Login: {
screen: Login,
navigationOptions: {
title: "Login",
header: {
visible: false,
},
},
},
// ... other screens here
})
1) To make the back button disappear in react-navigation
v5 or newer:
{
navigationOptions: {
title: 'MyScreen',
headerLeft: ()=> null,
// `headerLeft: undefined` should work too
// `headerLeft: null` should work but could trigger a TS error
}
NOTE:
v6 has an extra option:
headerBackVisible: false​
Whether the back button is
visible in the header. You can use it to show a back button alongside
headerLeft if you have specified it.
https://reactnavigation.org/docs/native-stack-navigator/#headerbackvisible
v2-v4:
navigationOptions: {
title: 'MyScreen',
headerLeft: null
}
2) If you want to clean navigation stack:
Assuming you are on the screen from which you want to navigate from:
If you are using react-navigation version v5 or newer you can use navigation.reset or CommonActions.reset:
// Replace current navigation state with a new one,
// index value will be the current active route:
navigation.reset({
index: 0,
routes: [{ name: 'Profile' }],
});
Source and more info here: https://reactnavigation.org/docs/navigation-prop/#reset
Or:
navigation.dispatch(
CommonActions.reset({
index: 1,
routes: [
{ name: 'Home' },
{
name: 'Profile',
params: { user: 'jane' },
},
],
})
);
Source and more info here: https://reactnavigation.org/docs/navigation-actions/#reset
For older versions of react-navigation:
v2-v4 use StackActions.reset(...)
import { StackActions, NavigationActions } from 'react-navigation';
const resetAction = StackActions.reset({
index: 0, // <-- currect active route from actions array
actions: [
NavigationActions.navigate({ routeName: 'myRouteWithDisabledBackFunctionality' }),
],
});
this.props.navigation.dispatch(resetAction);
v1 use NavigationActions.reset
3) For android you will also have to disable the hardware back button using the BackHandler:
http://reactnative.dev/docs/backhandler.html
or if you want to use hooks:
https://github.com/react-native-community/hooks#usebackhandler
otherwise the app will close at android hardware back button press if navigation stack is empty.
Additional sources: thank you to the users that added comments below and helped keeping this answer updated for v5.
Have you considered using this.props.navigation.replace( "HomeScreen" ) instead of this.props.navigation.navigate( "HomeScreen" ).
This way you are not adding anything to the stack. so HomeScreen won't wave anything to go back to if back button pressed in Android or screen swiped to the right in IOS.
More informations check the Documentation.
And of course you can hide the back button by setting headerLeft: null in navigationOptions
We need to set false to the gesturesEnabled along with headerLeft to null. Because we can navigate back by swiping the screen as well.
navigationOptions: {
title: 'Title',
headerLeft: null,
gestureEnabled: false,
}
You can hide the back button using left:null, but for android devices it's still able to go back when the user presses the back button. You need to reset the navigation state and hide the button with left:null
Here are the docs for resetting navigation state:https://reactnavigation.org/docs/navigation-actions#reset
This solution works for react-navigator 1.0.0-beta.7, however left:null no longer works for the latest version.
If your react navigation v6.x
options={{
title: "Detail Pembayaran",
headerTitleStyle:{
fontWeight:'bold',
},
headerBackVisible:false
}}
Reference : React document
using the BackHandler from react native worked for me. Just include this line in your ComponentWillMount:
BackHandler.addEventListener('hardwareBackPress', function() {return true})
it will disable back button on android device.
For react-navigation V6.0
<Stack.Screen
name={'Dashboard'}
component={Dashboard}
options={{
gestureEnabled: false,
headerShown: true,
headerLeft: () => <></>,
}}>
</Stack.Screen>
found it myself ;)
adding:
left: null,
disable the default back button.
const MainStack = StackNavigator({
Login: {
screen: Login,
navigationOptions: {
title: "Login",
header: {
visible: false,
},
},
},
FirstPage: {
screen: FirstPage,
navigationOptions: {
title: "FirstPage",
header: {
left: null,
}
},
},
react-navigation versions >= 1.0.0-beta.9
navigationOptions: {
headerLeft: null
}
For the latest version React Navigation 5 with Typescript:
<Stack.Screen
name={Routes.Consultations}
component={Consultations}
options={{headerLeft: () => null}}
/>
Since React Navigation v5.7, there's been a new official solution from the docs:
https://reactnavigation.org/docs/preventing-going-back
Use beforeRemove as a navigation listener to prevent back behavior from Android back button, header back button and custom back actions.
The best option to handle this situation is to use SwitchNavigator provided by React navigation. The purpose of SwitchNavigator is to only ever show one screen at a time. By default, it does not handle back actions and it resets routes to their default state when you switch away. This is the exact behavior that is needed in the authentication flow.
This is a typical way to implement it.
Create 2 stack navigators: One for authentication (sign in, sign up, forgot password, etc) and another for the main APP
Create a screen in which you will check which route from switch navigator you want to show (I usually check this in splash screen by checking if a token is stored in Async storage)
Here is a code implementation of above statements
import { createAppContainer, createSwitchNavigator } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import HomeScreen from "./homeScreenPath"
import OtherScreen from "./otherScreenPath"
import SignInScreen from "./SignInScreenPath"
import SplashScreen from "./SplashScreenPath"
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
export default createAppContainer(
createSwitchNavigator(
{
Splash: SplashScreen,
App: AppStack,
Auth: AuthStack,
},
{
initialRouteName: 'Splash',
}
)
);
Now in SplashScreen you will check the token and navigate accordingly
import React from 'react';
import {
ActivityIndicator,
AsyncStorage,
StatusBar,
StyleSheet,
View,
} from 'react-native';
class SplashScreen extends React.Component {
componentDidMount() {
this.checkIfLogin();
}
// Fetch the token from storage then navigate to our appropriate place
checkIfLogin = async () => {
const userToken = await AsyncStorage.getItem('userToken');
// This will switch to the App screen or Auth screen and this splash
// screen will be unmounted and thrown away.
this.props.navigation.navigate(userToken ? 'App' : 'Auth');
};
// Render any loading content that you like here
render() {
return (
<View>
<ActivityIndicator />
<StatusBar barStyle="default" />
</View>
);
}
}
Once you change routes in SwitchNavigator it removes the older route automatically and hence if you press the back button it will not take you to the auth/login screens anymore
I'm using v6, it's works for me:
<Stack.Screen
name="ApparelsHome"
component={ApparelsHome}
options={{
headerLeft: () => <></>,
}}
/>
We can fix it by setting headerLeft to null
static navigationOptions =({navigation}) => {
return {
title: 'Rechercher une ville',
headerLeft: null,
}
}
Simply doing
headerLeft: null
might be deprecated by the time you read this answer.
You should use following
navigationOptions = {
headerTitle : "Title",
headerLeft : () => {},
}
ReactNavigation v 5.0 - Stack option:
options={{
headerLeft: () => {
return <></>;
}
}}
TO REMOVE BACK HEADER BUTTON
options={{ headerBackVisible:false,}}
Full Tag
<Stack.Screen name="Name" component={NameScreen} options={{ headerBackVisible:false,}} />
The SwitchNavigator would be the way to accomplish this. SwitchNavigator resets the default routes and unmounts the authentication screen when the navigate action is invoked.
import { createSwitchNavigator, createStackNavigator, createAppContainer } from 'react-navigation';
// Implementation of HomeScreen, OtherScreen, SignInScreen, AuthLoadingScreen
// goes here.
const AppStack = createStackNavigator({ Home: HomeScreen, Other: OtherScreen });
const AuthStack = createStackNavigator({ SignIn: SignInScreen });
export default createAppContainer(createSwitchNavigator(
{
AuthLoading: AuthLoadingScreen,
App: AppStack,
Auth: AuthStack,
},
{
initialRouteName: 'AuthLoading',
}
));
After the user goes to the SignInScreen and enters their credentials, you would then call
this.props.navigation.navigate('App');
i think it is simple just add headerLeft : null , i am using react-native cli, so this is the example :
static navigationOptions = {
headerLeft : null
};
For latest version of React Navigation, even if you use null in some cases it may still show "back" written!
Go for this in your main app.js under your screen name or just go to your class file and add: -
static navigationOptions = {
headerTitle:'Disable back Options',
headerTitleStyle: {color:'white'},
headerStyle: {backgroundColor:'black'},
headerTintColor: 'red',
headerForceInset: {vertical: 'never'},
headerLeft: " "
}
In Latest Version (v2) works headerLeft:null. you can add in controller's navigationOptions as bellow
static navigationOptions = {
headerLeft: null,
};
For react-navigation version 4.x
navigationOptions: () => ({
title: 'Configuration',
headerBackTitle: null,
headerLayoutPreset:'center',
headerLeft: null
})
headerLeft: null
This won't work in the latest react native version
It should be:
navigationOptions = {
headerLeft:()=>{},
}
For Typescript:
navigationOptions = {
headerLeft:()=>{return null},
}
In react-navigation versions 5.x, you can do it like this:
import { CommonActions } from '#react-navigation/native';
navigation.dispatch(
CommonActions.reset({
index: 1,
routes: [
{ name: 'Home' },
{
name: 'Profile',
params: { user: 'jane' },
},
],
})
);
You can read more here.
Great Answers Provided Though, but i think this is quite simple
useEffect(() => {
props.navigation.addListener("beforeRemove", (e) => {
e.preventDefault();
});
}, [props.navigation]);
You can also do headerLeft:()=>false to get rid of back button
<Stack.Screen name="ImageScreen" component={ShowImage} options={{title:"SMAART", headerLeft:()=>false}} />
import React,{useEffect,useState,useRef} from 'react';
import { BackHandler,View } from 'react-native';
export default function App() {
useEffect(() => {
const backHandler = BackHandler.addEventListener('hardwareBackPress', () => true)
return () => backHandler.remove()
}, [])
return(
<View>
</View>
)}
I have working on old project where used React Navigation Version 4
I tried but only things work is ..
passwordEntry: {
screen: passwordEntry,
navigationOptions: {
gestureEnabled: false,
},
},
If you are using react native expo CLI then you can simply use
options={{headerBackVisible:false}}
You can use
options={{gestureEnabled: false}}
this you can declare where you are declaring your screen.
<AuthStack.Screen name="Dashboard" component={DashBoardContainer} options={{gestureEnabled: false}} />
This works for me

Resources