I cannot seem to nest navigators in a proper way - react-native

i'm developing an app in react-native, using react-navigation. I really love this library and the flexibility of it, but for the love of me i cannot seem to nest the navigators according to the schema i have.
The schema is actually not that complicated, so i'll try to explain it with words as well with a little schema.
My app will have a splash screen during which i'll do some logic (initialize the store, etc. etc.). The next screen is based on this logic, and can lead to a tutorial screen or the home itself.
Once the user has completed the tutorial it will be navigated to the home screen.
The user shouldn't be able to back to the tutorial from the home screen, should he come from there.
In the home screen i want a drawer navigator, that can lead to N screens . Each of this screen should just have a title and the back button, that brings back to the home.
One of this screen is the tutorial itself, which should have the same behaviour as the first time.
So to sum up:
Splash;
Tutorial (optional)
Home
Screen1;
Screen2;
....
Tutorial.
This is the code of my navigator:
import { createStackNavigator, createAppContainer, createDrawerNavigator, createSwitchNavigator } from 'react-navigation';
import HomeScreen from './home';
import Details from './details';
import SplashScreen from './splash';
import Tutorial from './tutorial';
const TutorialStack = createStackNavigator(
{ Tutorial },
{
mode: 'modal',
headerMode: 'none'
}
);
const DrawerNavigator = createDrawerNavigator(
{
Tutorial,
SplashScreen,
Home: HomeScreen,
Details
},
{
defaultNavigationOptions: {
drawerLockMode: 'locked-closed'
},
drawerBackgroundColor: 'rgba(255,255,255,.5)',
overlayColor: '#6b52ae',
contentOptions: {
activeTintColor: '#fff',
activeBackgroundColor: '#6b52ae'
}
}
);
const RootStack = createStackNavigator(
{
Home: HomeScreen,
DrawerNavigator
},
{
initialRouteName: 'Home',
defaultNavigationOptions: {
headerStyle: {
backgroundColor: '#f4511e'
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold'
}
}
},
);
const AppNavigator = createSwitchNavigator({
SplashScreen,
TutorialStack,
RootStack
}, {
mode: 'modal',
headerMode: 'none'
});
const AppContainer = createAppContainer(AppNavigator);
export default AppContainer;
I get the transitions almost correctly, but:
If i try to go back to the tutorial, i can (and i also inherit the header from the next navigator)
I can go back from a screen (e.g. Details) to home, and when i get there i get another 'back' arrow - this way i can go back and forth from home to details indefinetly.
I know i'm doing something wrong with the nesting, but i cannot seem to get it right.
Thanks to anyone who will help me!

Related

Using Stack Navigation with Drawer Navigation always ends up in home page

First of, sorry about the title. I did't know how else to explain what I am going for. I have a Drawer Navigation with a Stack Navigation inside of it. The reason why I did this is because I wanted a SinglePage and CommentPage which will be unmounted when left. And my main Navigation is the Drawer Navigation. The problem for me is that If I go in to one of the screens inside of the drawer navigation and from there, go in to the SinglePage (by tapping on an article), and now if I go back, it goes all the way back to home and not the previous page which is the other drawer navigation page.
I have already tried changing the order of my Routes but that did not help. Here is the Code I am working with:
//This is my Routes file which is located in my project root folder
//Here is the Stack Navigator
const SinglePageNavigator = createStackNavigator({
Home: { screen: Home },
SinglePage: { screen: SinglePage },
CommentPage: { screen: Comments },
},
{
initialRouteName: 'Home',
mode: "card",
headerMode: "none",
}
);
const styles = StyleSheet.create({
drawerLabelStyle: {
fontFamily: 'MV_Waheed',
fontWeight: '200',
fontSize: 16,
flex: 1,
alignItems: 'flex-end'
}
});
//And here is my Drawer Navigation. I have removed some screens
const TopLevelNavigator = createDrawerNavigator({
Home: {screen: SinglePageNavigator, navigationOptions: {
title: 'Home',
}},
Catpage: {screen: CatPage, navigationOptions: {title: 'News'}, params: {catID:1000, thisRouteName: 'News'}},
Catpage: {screen: CatPage, navigationOptions: {title: 'Entertainment'}, params: {catID:1000, thisRouteName: 'Entertainment'}},
},
{
hideStatusBar: false,
initialRouteName: 'Home',
drawerPosition: 'right',
drawerBackgroundColor: '#2a292d',
overlayColor: '#2a292d',
contentOptions: {
activeTintColor: '#fff',
inactiveTintColor:'#fff' ,
activeBackgroundColor: '#ff4136',
labelStyle: styles.drawerLabelStyle,
},
},
);
const AppContainer = createAppContainer(TopLevelNavigator);
So as shown, my Drawer navigation except for Home page takes in one page called CatsPage which then takes in a param and depending on that, it makes an API call and fetches data. Now Inside that, I have a button which goes to single page viewing the data of that article. Every time I go back, the singlepage gets unmounted. It works as intended. But the problem here is that if I am in page Entertainment and open an article from inside of there and go back, it goes back to home and not Entertainment. I can not wrap my head around of why that is. I did remove the initialRouteName: Home from Stack navigator thinking that might have been the issue but that did not help either. Any help is appreciated. Thanks!
Using the same page at the same time is not a good idea. It may not work as intended. And the same thing about routename is not good either.
You can try with 'createStackNavigator'
const MainNavigator = createStackNavigator({
Drawers: { screen: TopLevelNavigator }
});
const AppContainer = createAppContainer(MainNavigator);

React-Navigation showing Bottom menu and Drawer menu in the same screen

I am using React-Navigation, and I want to have a bottom tab menu and a drawer menu. After a bit of fiddling, I guess I am getting close but kind of stuck. Anyway here's my codes:
import { createStackNavigator,
createAppContainer,
createBottomTabNavigator,
createDrawerNavigator
} from 'react-navigation';
//import my screens... etc
class HomeScreen extends React.Component {
//Other contents.....
//Create my main navigation stacks here
const Home = createStackNavigator({
HomeScreen,
Screen1,
Screen2,
SettingScreen,
ProfileScreen,
//......etc.
});
//navigation stack for the bottom tab menu
const CalendarScreen = createStackNavigator({ myCalendar });
const GraphScreen = createStackNavigator({ myGraph });
//Botton Tab menu
const TabNavigator = createBottomTabNavigator({
Home, CalendarScreen, GraphScreen
});
//Drawer menu
const DrawerNavigator = createDrawerNavigator(
{
Tab: { screen: TabNavigator },
Setting: { screen: SettingScreen },
Profile: { screen: ProfileScreen }
},
{
drawerWidth: 300,
drawerPosition: 'right',
}
);
export default createAppContainer(DrawerNavigator);
}
The result is that the screen loads with the Bottom menu showing, and swiping from the right side will open the Drawer menu.
The problem is that it displays "Tab" as one of the menu in the Draw menu. And if I click on the "Setting" or "Profile", it will close/hide the Bottom menu. I had to click on "Tab" in the Drawer menu to make the Bottom menu show again.
What I want to achieve is to have the Bottom menu always showing, and a drawer menu with only "Setting" and "Profile". How can I achieve this?
I'm having this same issue while developing using React Native.
I read some issues on their (react-navigation) GitHub and some Stack Overflow answares as well but nothing helped much.
My solution to this was to create a StackNavigation with all my screens and then a Drawer Navigation only with the ones I wanted to put in the drawer (unfortunately this keeps the problem of showing the "Home" link in the drawer).
This is my Routes.js
import HomeScreen from "./components/HomeScreen";
import ProfileScreen from "./components/ProfileScreen";
import AnimalsScreenfrom "./components/AnimalsScreen";
import DoctorsScreenfrom "./components/DoctorsScreen";
import { createStackNavigator, createDrawerNavigator } from "react-navigation";
const BottomNavigator = createStackNavigator(
{
Home: { screen: HomeScreen, name: "Home" },
Profile: { screen: ProfileScreen, name: "Profile" },
Animals: { screen: AnimalsScreen, name: "Animals" },
Doctors: { screen: DoctorsScreen, name: "Doctors" }
},
{
headerMode: "none"
}
);
const AppNavigator = createDrawerNavigator({
Mapa: BottomNavigator,
Animals: { screen: AnimalsScreen, name: "Animals" },
Doctors: { screen: DoctorsScreen, name: "Doctors" }
});
export default AppNavigator;
After that, in all my components, I manually added the buttons as if they were regular BottomTabNavigator. Each of the buttons is binded on their onPress function to navigate to a different screen, so it simulates pretty well the function.
Something like this (it is not styled, jut setted to be on the bottom of the screen yet):
<View style={{ flex: 1, position: "absolute", bottom: 0 }}>
<Button
title="Menu"
onPress={() => this.props.navigation.openDrawer()}
/>
<Button
title="Home"
onPress={() => this.props.navigation.navigate("Home")}
/>
<Button
title="Profile"
onPress={() => this.props.navigation.navigate("Profile")}
/>
</View>
My first button opens the Drawer, the second goes to the HomeScreen and the third to the profileScreen while in my drawer I have the first button to Home, the second to AnimalsScreen and the third to DoctorsScreen
I am still developing this app, but you can check the code in my github Repository:
React-native-sample-app. Some of the components names are different but the logic is the same.
Hope it helps

Unmount or re-render screen in drawer navigator

I just added drawer navigator recently and wrapped my screens in createDrawerNavigator()
These are my current routes:
Home.js
Post.js
Settings.js
When a user navigates from Home to Post I pass a param that has the post data.
onPress={() => this.props.navigation.navigate('Post', {postData: postData})}
When ever the user goes back to Home from Post, then post will be unmounted. and mounted back again with fresh data when another post is clicked.
My issue is that with implementing the drawer, the Post screen does not get unmounted when navigating back to home, I keep gettings the same props and screen of the first post opened, over and over again.
This is my route setup:
import React from "react";
import { createDrawerNavigator, createStackNavigator, createAppContainer } from 'react-navigation';
import Home from './screens/Home';
import Post from './screens/Post';
import Settings from './screens/Settings';
import SideBar from './screens/sidebar';
const Drawer = createDrawerNavigator(
{
Home: {screen: Home},
Post: {screen: Post},
Settings: {screen: Settings}
},
{
initialRouteName: "Home",
backBehavior: 'initialRoute',
contentOptions: {
activeTintColor: "#e91e63"
},
contentComponent: props => <SideBar {...props} />,
}
);
const AppNavigator = createStackNavigator(
{
Drawer: {screen: Drawer},
},
{
initialRouteName: "Drawer",
headerMode: "none",
}
);
export default createAppContainer(AppNavigator);
What am I doing wrong?
I want each post screen to open and re render as new when navigating to it from Home.
For those using react-navigation v5. I faced the same issue my component was not unmounting by using goBack() for a screen link in drawer. After little research I found our that in latest version of react-navigation they have added an attribute unmountonblur in screen options for drawer navigator by default its false that's why component doesn't unmount. I am using this to solve my problem.
Here is my code
<Drawer.Screen name="ResetLogsStack" component={ResetLogsStack} options={{unmountOnBlur:true}}/>
Here is the link for that: unmountonblur
I use to face the same issue. I made my screen Post listen to navigation focus event triggered by react-nativation here instead of componentDidMount.
import React from 'react';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
const Post = () => (
<View>
<NavigationEvents
onWillFocus={payload => console.log('will focus',payload)}
onDidFocus={payload => console.log('did focus',payload)} //
onWillBlur={payload => console.log('will blur',payload)}
onDidBlur={payload => console.log('did blur',payload)}
/>
{/*
Your view code
*/}
</View>
);
With onDidFocus, you may get the navigation param, fetch data, and/or update state. You may clear screen state with onDidBlur if needed.
Alternatively, you can do imperative coding as this doc here
Update :
By the way, I am wondering why you put Post with Drawer? Is it just to have a link in the drawer that can access to the Post page?
In my opinion, you should move Home and Post to new stack and make Home as initial Route. This will make sure that the Post is unmounted after navigating back to Home.
Check out my sample below
const HomeStack = createStackNavigatior({
Home: {screen: Home},
Post: {screen: Post},
}, {
initialRouteName: 'Home',
...
})
const Drawer = createDrawerNavigator(
{
HomeStack
Settings: {screen: Settings}
},
{
initialRouteName: "HomeStack",
backBehavior: 'initialRoute',
contentOptions: {
activeTintColor: "#e91e63"
},
contentComponent: props => <SideBar {...props} />,
}
);
It's the way react-navigation works, to counter this you can add listeners, like so:
<NavigationEvents
onDidFocus={payload => { console.log(payload.state.params,'<- this has your new params') }}
/>
OR
this.props.navigation.addListener('didFocus', payload=>{console.log(payload)})
Check this for more information
I lost several hours on this after updating React Navigation from 2.14.2 to 3.11.0. The funny thing is that it was working perfectly and after upgrade pages were not re-rendering which was causing to wrong language and not actual data on the pages.
What I have found is the issue DrawerNavigator screen shouldn't unmount
So part of us complain why it is not rerendering screens and part was complaining why it was rerendering. The second part won the battle :)
Basically the solution was as expected: to listen to WillFocus event...
Just add this to code to your component if you want it to be rerendered on each visit.
/**
* When using React Navigation Component is not umnounted and instead stored in navigator.
* We need component to be rerendered during each visit to show right info and language
*/
componentDidMount() {
//this.props.navigation will come in every component which is in navigator
focusSubscription = this.props.navigation.addListener(
'willFocus',
payload => {
this.forceUpdate();//Native react function to force rerendering
}
);
this.setState({focusSubscription: focusSubscription});
}
componentWillUnmount() {
this.state.focusSubscription.remove();
}
I tried to use the method suggested in the answers but they didn't work perfectly with my case.
Solution:
I tried to flip createDrawerNavigator() with createStackNavigator() and it worked perfectly like before!
I don't have to use react navigation events, normal event like componentWillUnmount and componentWillMount work perfectly.
const MainScreens = createStackNavigator(
{
Home: {screen: Home},
Post: {screen: Post},
Settings: {screen: Settings}
},
{
initialRouteName: "Home",
headerMode: "none",
}
);
const AppNavigator = createDrawerNavigator(
{
Main: {screen: MainScreens},
},
{
initialRouteName: "Main",
backBehavior: 'initialRoute',
contentOptions: {
activeTintColor: "#e91e63"
},
contentComponent: props => <SideBar {...props} />,
}
);
export default createAppContainer(AppNavigator);
Not sure if there's anything wrong in what I did, but its working fine until now.
As per Muhammad Zain response, if you are using react navigation v5, you can pass directly to the Drawer screen the option unmountOnBlur
options={{unmountOnBlur:true}}
and that will be enough.
React navigation docs
I'm using react-navigation for months. But placing screens directly into Drawer navigator prevents willunmount method to be called. First place screen in a stack navigator then place it into drawer navigator.

How to implement Drawer and TabBar in StackNavigator

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 ?

Push from SingleScreen to TabBased in Wix React Native Navigation

There are documentation and examples of navigating from tab view to other single views. But I can't find any information about navigating from a single view to a tab view.
Is there a possibility to this.props.navigator.push to view with tabs somehow?
If not is there are some workarounds to achieve that?
You should do something like this:
Your main stack navigator:
import HomeScreenTabs from '../yourpath/TabNavigator';
const MainStack = createStackNavigator({
HomeScreen: {
screen: HomeScreenTabs,
},
SingleViewScreen1: {
screen: SomeSingleViewScreen,
navigationOptions: {...}
},
SingleViewScreen2: {
screen: SomeOtherSingleViewScreen,
navigationOptions: {...}
},
}
Your tab navigator:
//Your 3 tab screens
import Home from '../Home';
import Profile from '../Profile ';
import Feedback from '../Feedback ';
const Tabs = createBottomTabNavigator(
{
Home: {
screen: Home
},
Profile: {
screen: Profile
},
Feedback: {
screen: Feedback
}
},
export default Tabs;
And in order to navigate to the Tabs screen from e.g. SingleViewScreen1 you would do this.props.navigation.navigate('HomeScreen').
Use a Wix Navigation v2. It's pretty stable even it says that it is an alpha release. It solves this issue.