I've been assigned to a project where we need to upgrade react navigation from version 3 to version 6. Looking at the code I've found some strage stuff (or at least I think they are strange), the project consists of a Switch Navigator with two Stak Navigators inside like this:
const NavigatorOne = createStackNavigator(
{
NavOneScreenOne: {screen: NavOneScreenOne},
NavOneScreenTwo: {screen: NavOneScreenTwo},
NavOneScreenThree: {screen: NavOneScreenThree},
NavOneScreenFour: {screen: NavOneScreenFour}
},
{
headerMode: 'none',
navigationOptions: {
header: null,
},
defaultNavigationOptions: {
gesturesEnabled: false,
},
}
);
const NavigatorTwo = createStackNavigator(
{
NavTwoScreenOne: {screen: NavTwoScreenOne},
NavTwoScreenTwo: {screen: NavTwoScreenTwo},
NavTwoScreenThree: {screen: NavTwoScreenThree},
NavTwoScreenFour: {screen: NavTwoScreenFour}
},
{
headerMode: 'float',
navigationOptions: {},
defaultNavigationOptions: {
header: <Header />,
gesturesEnabled: false,
},
}
);
export default createAppContainer(
createSwitchNavigator(
{
App: NavigatorOne,
Visits: NavigatorTwo,
Login: PageLogin,
},
{
initialRouteName: 'Login',
backBehavior: 'none',
}
)
);
Then I have an AppNavigatorContainer, like this one:
const AppNavigatorContainer = ({ setNavigator }: { setNavigator: (n: NavigationContainerComponent) => void }) => {
const dispatch = useDispatch();
return (
<AppNavigator
ref={(navigatorRef) => {
NavigationService.setTopLevelNavigator(navigatorRef);
setNavigator(navigatorRef);
}}
onNavigationStateChange={(_prevState, currentState) => dispatch(setCurrentRoute(NavigationService.getActiveRouteName(currentState)))}
/>
);
};
and the App file like this:
const [navigator, setNavigator] = useState(null);
return (
<Provider store={ReduxConf.store}>
<PersistGate loading={null} persistor={ReduxConf.persistor}>
<ReduxNetworkProvider pingInterval={20000}>
<Root>
<StyledView>
<StatusBar hidden />
<Header />
<AppNavigatorContainer setNavigator={setNavigator} />
<Toolbar />
<Menu />
...
</StyledView>
...
</Root>
</ReduxNetworkProvider>
</PersistGate>
</Provider>
);
So basically its saving a reference to the AppNavigator inside a global file (NavigationService) so that it can be accessed everywhere, including Menu and Toolbar which aren't nested inside AppNavigatorContainer. Toolbar is a toolbar set at the bottom of the screen which changes buttons according if you are in the first or second stack.
As I see it, toolbar should be built using two TabNavigators (each one with its set of buttons/tabs) inside a StackNavigator, and Menu should be a DrawerNavigator (don't know if in version 3 of react navigation had these stacks), and should be nested inside the SwitchNavigator (in version 6 theres no Switch but the concept remains the same).
Is this a poorly written navigation structure? it feels it breaks many good coding rules... (the current structure of the project)
Related
I always use react-native-router-flux for navigation, but on this project I need to use react-navigation and I got some troubles with it. I need to implement drawer and tabBar inside stack navigator.
Problems:
I use header component from native-base library but i can't open
drawer.
How to use my own customized component for drawer and tabBar?
Maybe I need to chage structure. I will consider any recommendations how to improve structure.
I used version 3 of react-navigation.
My code:
const AppStackNavigator = createStackNavigator({
loginFlow: {
screen: createStackNavigator({
intro: { screen: Intro },
login: { screen: Login },
registration: { screen: Registration },
}),
navigationOptions: {
header: null
}
},
mainFlow: {
screen: createStackNavigator({
MyDrawer: createDrawerNavigator({
Dashboard: {
screen: Home,
},
first: {
screen: first,
},
second: {
screen: second
},
third: {
screen: third
},
last: {
screen: last
}
}),
// settings: { screen: SettingsScreen },
someTab: {
screen: createBottomTabNavigator({
main: { screen: Home },
firsrTab: { screen: Screen1 },
secondTab: { screen: Screen2 },
thirdTab: { screen: Screen3 },
nextTab: { screen: Screen4 }
}),
navigationOptions: {
header: null
},
}
}),
navigationOptions: {
header: null
}
}
});
const AppContainer = createAppContainer(AppStackNavigator);
import React from 'react';
import { Header, Left, Icon, Right } from 'native-base';
const CustomHeader = (props) => {
return(
<Header>
<Left>
<Icon
name='menu'
onPress={() => {this.props.navigation.openDrawer()}}
/>
</Left>
</Header>
)
}
export { CustomHeader }
You might wanna consider the SwitchNavigator for the authentication flow instead of a Stack at the top as it replaces the routes so that you can never navigate back to the login/signup/splash once you get into the application and for accessing Tabs and Drawer inside stack/switch, you can wrap the Drawer inside your top level navigator and tab inside the drawer.
So you root navigation would look like this.
export default RootNavigation = createSwitchNavigator({
LoginScreen: {screen: LoginContainer},
Application: {screen: AppDrawer},
});
Your drawer navigator should be like the following:
const AppDrawer = createDrawerNavigator({
ApplicationTab: {screen: TabBar},
... other screen that you might want to use in drawer navigation.
}, {
contentComponent : (props) => <MyCustomDrawer {...props} />
});
and, Tab Navigator would be,
const TabBar = createBottomTabNavigator({
TabScreen1: {screen: Tab1},
... other tabs...
}, {
tabBarComponent : (props) => <MyTabBar {...props} />
});
If you put each of those navigators in single file then please do declare Tab before Drawer and Drawer before the Switch, else it would give errors.
In my experience, customising drawer navigator is very simple and fruitful but customising tab is not, there aren't proper API doc for the same and community answers are also somewhat misleading.
BUT, with normal use cases and for most of the vivid ones too, you can do your job without needing to override the default one as it is already highly operable and customisable in terms of icons, materialism and each tab exposes its on onPress that can also be easily overriden.
and as you as the drawer is not getting operated from/via the header, then can you please ensure that the navigation prop you are using to operate the drawer open close or toggle action is the one given by drawer ?
Everyone I am facing an issue in react-native as I am new to this .
I am callling a tabbpage from homePage
so in tabpage there is a navbar at the top,below this navbar a tabbar is showing two tabs.
This is good till now but the problem starts from here
In tabPage I have two tabs ->tab1 and tab2
from tab1 I am navigating to page MainPage1 where it is showing a Navbar below navbar a tabbar below tabbar an another navbar. As shown in the picture.
I am totally unable to remove both the top level navbar having title "Stopages" and the tabbar.
I am using Tabview for creating this tabbpage and using stacknavigator for navigating to different pages.I am stuck here and Not able to find solution
NOTE->I have tried using
navigationOptions: {
tabBar: ({ state }) => ({
visible: false
})
but its not doing anything Please help
class TabPage extends React.Component{
state = {
index: 0,
routes: [
{ key: 'Stopagess', title: 'Stopages' },
{ key: 'MapStopagess', title: 'Maps' },
],
};
render() {
return (
<TabView
navigationState={this.state}
renderScene={SceneMap({
Stopagess: Stopages,
MapStopagess: MapStopages,
})
}
renderTabBar={props =>
<TabBar
{...props}
style = {{backgroundColor:'#3f51b5'}}
indicatorStyle={{ color: 'pink' }}
/>
}
onIndexChange={index => this.setState({ index })}
initialLayout={{ width: Dimensions.get('window').width }}
indicatorStyle={{ backgroundColor: 'pink' }}
/>
);
}
}
This is my Stopages class
class Stopages extends Component {
render()
{
return (
<StopageDetail/>
)
}
}
const StopageDetail = createStackNavigator({
Stp:{
screen: Stpforsomeissue,
navigationOptions: () => ({
header:null,
tabBarVisible: false,
}),
},
NextDetailStopage:{
screen :StopageDetailOfStudents,
navigationOptions: ({ navigation, screenProps }) => ({
title: 'Stopages Detail',
// tabBarVisible: navigation.state.params=false,
headerStyle: { backgroundColor: '#ffd600'},
/>,
})
}
})
I believe you are using createMaterialTopNavigator under a root stack with routeName Stopages and under each Tab route you have a dedicated stack for each one, the first one being the Stopages Detail. Please correct me if thats not the case and edit your post to show the navigation stack you have written.
This might be useful to you:
The root stack can be like the one below:
createStackNavigator ({
StopPages: {screen: MyTabNavigator, navigationOptions: {header: null}}
});
and the TabNavigator will be like this:
const MyTabNavigator = createMaterialTopTabNavigator({
Stopages: {screen: StopagesStack},
Maps: {screen: MapsStack},
});
const StopagesStack = createStackNavigator({
StopagesDetail: {screen: DetailContainer, navigationOptions: {header: null}}
});
The key to hide default stack navigator's header is to set it null in the navigationOptions.
You can play around within navigationOptions within a tab of createMaterialTopTabNavigator in order which ones had TopTabBar or not, Example:
export const Dashboard = createMaterialTopTabNavigator({
First: {
screen: FirstScreen, // This is my first Tab (a View)
navigationOptions: {
title: 'First'
}
},
Second: {
screen: SecondStack, // This is another Stack
navigationOptions: ({ navigation }) => ({
title: 'SecondStack',
tabBarVisible: navigation.state.index === 0, //Tab bar will be visible depending of the state (you can put another index)
animationEnabled: true
})
},
Third: {
screen: ThirdScreen, // This is my third Tab (a View)
navigationOptions: {
title: 'ThirdScreen',
}
}
});
I want to change the bottom tabs on the screen based on what features are enabled. This feature list is populated via a login API call.
Currently I have the following:
const TabRouteConfig = {
Home: DashboardScreen,
FeatureA: FeatureA,
FeatureZ: FeatureZ,
};
const TabNavigatorConfig = {
initialRouteName: 'Home',
order: [
'Home',
'Feature A',
],
tabBarPosition: 'bottom',
lazy: true,
};
const TabNavigator1 = createBottomTabNavigator(
TabRouteConfig,
TabNavigatorConfig,
);
// Set the tab header title from selected route
// https://reactnavigation.org/docs/en/navigation-options-resolution.html#a-stack-contains-a-tab-navigator-and-you-want-to-set-the-title-on-the-stack-header
TabNavigator1.navigationOptions = ({ navigation }) => {
const { index, routes } = navigation.state;
const { routeName } = routes[index];
return {
headerTitle: routeName,
};
};
const TabNavigator2 = createBottomTabNavigator(
TabRouteConfig,
{
...TabNavigatorConfig,
order: [
'Home',
'Feature Z',
]
);
TabNavigator2.navigationOptions = TabNavigator1.navigationOptions;
const Stack = createStackNavigator({
Main: {
screen: props => (props.screenProps.hasFeature ?
<TabNavigator1 /> : <TabNavigator2 />
)
},
})
const WrappedStack = props => (
<View style={styles.container}>
<Stack
screenProps={{ hasFeature: props.hasFeature }}
/>
</View>
);
const mapStateToProps = (state, props) => {
return {
...props,
hasFeature: state.hasFeature,
};
};
export default connect(mapStateToProps, null)(WrappedStack);
This mostly works - it dynamically switches between TabNavigator1 and TabNavigator2 based on hasFeature, but it no longer honors the navigationOptions placed on the TabNavigators and the headerTitle is not set.
Is there a better way to do this?
It's an antipattern to render more than one navigator simultaneously as the navigation states of those navigators will be completely separated, and you will not be able to navigate to one from another.
You can use tabBarComponent option to achieve what you want. Hope you can get the idea from below example:
import { createBottomTabNavigator, BottomTabBar } from 'react-navigation-tabs';
const TabNavigator = createBottomTabNavigator(
TabRouteConfig,
{
tabBarComponent: ({ navigation, ...rest }) => {
const filteredTabNavigatorRoutes = navigation.state.routes.filter(route => isRouteAllowed(route));
return (
<BottomTabBar
{...rest}
navigation={{
...navigation,
state: { ...navigation.state, routes: filteredTabNavigatorRoutes },
}}
/>
);
},
},
);
NOTES:
You don't have to install react-navigation-tabs separately. It is automatically installed with react-navigation 2.0.0+.
isRouteAllowed is the function which returns true or false based on whether to show that route or not. Make sure to only check the top level routes in that object.
TabRouteConfig should contain all possible tabs, and this logic only hides the route from the TabBar visually. So, you can still programmatically navigate to all routes. Therefore, you might need additional logic in each of those screens to decide whether to render them based on hasFeature.
Im using react-navigation to build my app, I want to have both tab and stack navigation so I did this:
const FindPage = StackNavigator({
Find: {
screen: Find,
},
Item:{
screen:Item
}
}, {
initialRouteName: 'Find',
});
const ProfilePage = StackNavigator({
Profile: {
screen: Profile,
},
Item:{
screen:Item
}
}, {
initialRouteName: 'Profile',
});
const MyApp = createBottomTabNavigator({
Find: FindPage,
Profile: ProfilePage
}
});
const auth = StackNavigator({
Login:{
screen: Login,
},
Register:{
screen: Register,
},
Main:{
screen: MyApp,
}
},{
initialRouteName: 'Main',
headerMode: 'none'
});
export default auth;
But I dont get it well. this is what the screenshot is
giving:
enter image description here
if you see the tab lost it tab icon and font when im using stacknavigation in tab navigation, this worked for me in another version of react nvigation and cant find anything on the web Please Help !
with reactnavigation2 you can achieve this like in the below code
Read more about it here https://reactnavigation.org/docs/en/bottom-tab-navigator.html
import Ionicions from "react-native-vector-icons/Ionicons";
screen: createBottomTabNavigator(
{
HomeScreen: {
screen: HomeStack,
navigationOptions: {
tabBarLabel: props => <Label name="Home" {...props} />,
tabBarIcon: props => (
<Icon name="ios-home-outline" fillname="ios-home" {...props} />
)
}
}
})
I have an issue that no matter what I try, I just cannot get styling to work on my Stack with Drawer navigator. I've tried several examples and solutions but just none of them work, my navigation stays the default blue.
In my current code below, I've left 2 coloring options open - those two are of the many things I've tried before.
I hope that someone knows what's going on because I'm starting to get clueless.
const MainScreenNavigator = StackNavigator({
'Home': {
screen: Components.HomeScreen,
navigationOptions: ({navigation}) => ({
header: <Components.StackHeader title='Home' navigation={navigation} />
})
},
'Scan': {
screen: Components.ScanScreen,
navigationOptions: ({navigation}) => ({
header: <Components.StackHeader title='Scan QR' navigation={navigation} />
})
},
'LockInfo': {
screen: Components.LockInfoScreen,
navigationOptions: ({navigation}) => ({
header: <Components.StackHeader title='Lock' navigation={navigation} />
})
},
});
const RootNavigator = DrawerNavigator ({
Home: {
screen: MainScreenNavigator,
},
});
RootNavigator.navigationOptions = {
contentOptions: {
activeBackgroundColor: '#ff5976',
style: {
backgroundColor: '#000000'
}
}
}
const ModalNavigator = StackNavigator(
{
Main: { screen: Main },
Login: { screen: Login },
},
{
headerMode: 'none',
mode: 'modal',
navigationOptions: {
gesturesEnabled: false,
},
https://reactnavigation.org/docs/navigators/stack#Screen-Navigation-Options
checkout this above link, search and add the required styling properties inside your own StackNavigator, headerStyle, headerTitleStyle, headerBackTitleStyle, headerTintColor and so on...
Also for DrawerNavigator, checkout this below link and replicate the code, styling properties for the same are provided.
https://reactnavigation.org/docs/navigators/drawer#DrawerNavigator
Also remove the single inverted commas from Home, Scan and LockInfo