React-native nested navigators within Redux - reactjs

I've decided that I need to swap my regular react-nagivation setup for a redux container, mostly because I need to keep track of stuff like login status etc.
I won't lie that since it's my first react navigation moving it into redux is a hell lot of confusion for me, but step by step I've got to the point where my basic structure is working, well... almost working.
What I have is a StackNav on top of everything, with the main screen being a TabNav. The screen is loading fine, and parts of the app that switch to different screen of that top-level StackNav are working fine. My problem occurs when I switch from 1st tab to the 2nd within that TabNav.
To clarify, I've got 2 tabs - 'Home' and 'My Account'.
Switching from home to my account does look like tabs are being switched, but the component is not being rendered properly. It's missing all that's within the render function for it together with the styles. Then, when I want to switch back to 'Home' tab, the screen slides like I would actually switch between top-level StackNav, instead of just switching between tabs. On top of that, Now after being back to 'Home' tab, I've got the option to 'go back' in the top left corner which when used, just slides to the 'previous' view of Home, even tho it's the same tab.
I'm confused while writing that but hopefully someone will be able to see sense in that. Here's my code:
top level Stack Nav config:
const routeConfiguration = {
Home: { screen: HomeTabsNavigation },
GameList: { screen: GameList },
OfferDetails: { screen: OfferDetails },
}
const stackNavOptions = {
initialRouteName: 'Home',
mode: 'card',
headerMode: 'float',
}
export const StackNavConfig = StackNavigator(routeConfiguration, stackNavOptions);
top level Stack Nav component:
const mapStateToProps = (state) => {
return {
navigationState: state.stackNav,
}
}
class StackNavigation extends React.Component {
render() {
const { dispatch, navigationState } = this.props
return (
<StackNavConfig
navigation={
addNavigationHelpers({
dispatch,
state: navigationState,
})
}
/>
)
}
}
export default connect(mapStateToProps)(StackNavigation)
HomeTabs defined as the first screen of top-level StackNav:
const HomeTabs = TabNavigator({
Home: { screen: HomeView },
MyAccount: { screen: MyAccountView },
}, {
tabBarOptions: tabBarOptions,
});
export default HomeTabs;
HomeTabs component:
const mapStateToProps = (state) => {
return {
navigationState: state.homeTabs,
}
}
class HomeTabsNavigation extends Component {
render() {
const { navigationState, dispatch } = this.props;
return (
<HomeTabs
navigation={
addNavigationHelpers({
dispatch,
state: navigationState,
})
}
/>
);
}
}
export default connect(mapStateToProps)(HomeTabsNavigation);
My Redux store:
const middleware = () => {
return applyMiddleware(createLogger());
}
export default createStore(
combineReducers({
stackNav: (state, action) => StackNavConfig.router.getStateForAction(action, state),
homeTabs: (state, action) => HomeTabs.router.getStateForAction(action, state),
}),
middleware(),
)
My TabNav views are just displaying text for now so I won't put them here unless it's important, then I'll of course update the code.
Help! ;)

Ok so I've found the issue. The problem was that I had same route name defined in both top level Stack Navigator and nested Tab Navigator.
As you can see here:
const routeConfiguration = {
Home: { screen: HomeTabsNavigation },
GameList: { screen: GameList },
OfferDetails: { screen: OfferDetails },
}
const stackNavOptions = {
initialRouteName: 'Home',
mode: 'card',
headerMode: 'float',
}
export const StackNavConfig = StackNavigator(routeConfiguration, stackNavOptions);
and here:
const HomeTabs = TabNavigator({
Home: { screen: HomeView },
MyAccount: { screen: MyAccountView },
}, {
tabBarOptions: tabBarOptions,
});
export default HomeTabs;
It seems that these routes are maybe merged together at some point and whenever my Tab Nav was trying to go 'back' to his 'Home' screen it was the top-level Stack Nav taking over the control and loading it's own 'Home' screen on top of what was there before, creating exactly what it is which is a Stacked View of screens. I've renamed it to 'TabsHome' and everything is working fine. Solved!

Related

How to create dynamic tabs in React Navigation v3

I have stack navigator wrapped with app container:
const AppNavigator = createStackNavigator({
Home: {
screen: Home,
},
});
export default createAppContainer(AppNavigator);
Home should have dynamic tabs. I want to load some info from backend and then generate tabs.
And here is my Home component:
class Home extends Component {
get tabs() {
return {
Main: { screen: Demo },
World: { screen: Demo },
};
}
get tabOptions() {
return {
// options...
};
}
render() {
const Tabs = createMaterialTopTabNavigator(this.tabs, this.tabOptions);
return <Tabs />;
}
}
In this case I am getting this error:
But if I am wrapping with createAppContainer...
const Tabs = createAppContainer(createMaterialTopTabNavigator(this.tabs, this.tabOptions));
...then I am getting warning about more than one container in app.
So how to make dynamic tabs in a right way?
UPD 1. Real code of getting tabs which I use now with yellow warning.
get tabs() {
const { categories } = this.props;
return reduce((acc, item) => assoc(prop('name', item), Demo, acc), {})(categories); // ramda
}
That error shows up because wrapping a navigator as a component is antipattern since the navigation prop is being broken that way.
instead, don't use a class , use plain functions, or you are gonna struggle so much like the other guys that tried to use a component
function tabs(){
return {
Main: { screen: Demo },
World: { screen: Demo },
}
}
function tabOptions(){
return {
// options...
};
}
export default createMaterialTopTabNavigator(tabs(), tabOptions())

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.

React Navigation confusion

I started learning React Native, I know how to create TabNavigator or DrawerNavigator but I need access to props.navigation on landing page where I don't have TabNavigation, how I'am supposed to get it.
I tried this:
const App = createStackNavigator({
LandingScreen: { screen: LandingScreen },
Register: { screen: RegisterScreen },
Login: { screen: LoginScreen}});
But still, this.props are empty object.
And what is stackNavigator in plain language, does it just define navigation in order to be able to use navigation ?
In your LandingScreen you can able to get the this.props.navigation. You can show your TabNavigation as your initial route. You can even set Stack Navigation for each Tab. For React Navigation(v2) Documentation https://reactnavigation.org/docs/en/tab-based-navigation.html
Stack Navigation:
Provides a way for your app to transition between screens where each new screen is placed on top of a stack.
//Stack Navigator
const App = createStackNavigator({
Home: { screen: Tabs }, //You can nest your TabNavigator here, Hence the LandingScreen inside your HomeStack will shown as your Initial screen
Register: { screen: RegisterScreen },
Login: { screen: LoginScreen}});
//Sample Tab Navigator
const Tabs = createBottomTabNavigator(
{
Home: HomeStack,
Settings: SettingsStack,
},
{
/* Other configuration remains unchanged */
}
);
const HomeStack = createStackNavigator({
LandingScreen: LandingScreen,
Details: DetailsScreen,
});
const SettingsStack = createStackNavigator({
Settings: SettingsScreen,
Details: DetailsScreen,
});
You can get your navigation Prop by using below
//LandingScreen Component
export default LandingScreen extends Component{
static navigationOptions = ({navigation}) => ({ //you can able to destructure your navigation prop here
headerTitle: 'Langing Page'
})
render(){
return(
//your logic here
)
}
}

Rendering both drawer and tabbed navigation with react navigation

I'm using React Navigation in my react native app, and I'm looking to render both the drawer navigation and tab navigation components simultaneously.
At first, I tried to render both in the root app component, but that threw an error that appears in their documentation here. I then attempted the solution listed below, which produced the following for me:
const TabNavigator = createBottomTabNavigator({
Settings: {
screen: SettingsScreen
}
});
const AppNavigator = createDrawerNavigator({
Home: {
screen: HomeScreen
},
Products: {
screen: ProductsScreen
},
TabNav: TabNavigator
});
export default class App extends React.Component {
render() {
return <AppNavigator />;
}
}
The tab navigation only shows when I select the TabNav link in the drawer navigation. I want it on every screen. I also don't want the TabNav label to show in the drawer navigation.
Am I missing something in the documentation, or is this the intended functionality?
For the first question, move the TabNav to first in the navigator like this
TabNav: TabNavigator,
Home: {
screen: HomeScreen
},
Products: {
screen: ProductsScreen
}
or else you change the initialRouteName to "TabNav"
For the Second question,
If you want the TabNavigation to show inside the HomeScreen and ProductsScreen you need to include TabNavigation in both
or they need to have a parent TabNavigation which contains those. I can add a sample code if you post what the TabNav contains.
And for the Third question,
You can use contentComponent in DrawerNavigator example like this
contentComponent: props => (
<AppDrawerContent {...props} navigation={props.navigation} />
),
Edited:
If you want TabNavigator to show on each one. I think you should change the way you Structure your navigator.
export default class App extends React.Component {
render() {
return < TabNavigator />;
}
}
const TabNavigator = createBottomTabNavigator({
Drawer: {
screen: AppNavigator
}
});
const AppNavigator = createDrawerNavigator({
Home: {
screen: HomeScreen
},
Products: {
screen: ProductsScreen
}
});
or else if you want TabNavigator inside each screen individually then
const AppNavigator = createDrawerNavigator({
HomeNavigator: {
screen: HomeScreenNavigator
},
Products: {
screen: ProductsScreen
}
});
const HomeScreenNavigator = createBottomTabNavigator({
Home: {
screen: HomeScreen
},
Settings: {
screen: SettingsScreen
}
});
export default class App extends React.Component {
render() {
return <AppNavigator />;
}
}
Like wise for Products screen also.
What you have actually done is you included the TabNav as Drawer Screen so it does appear in the Drawer side bar.
Even if this structure doesn't work you need to check on restructuring it. Or you may give a images how you want to show it. May be I could help you with better understanding.

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
}
})

Resources