Reputation: 4803
I'm relatively sure I found out it isn't possible, but I want to make sure there isn't a way.
The app in question starts off with an AppNavigator StackNavigator
.
export const AppNavigator = StackNavigator({
Login: {
screen: Login,
navigationOptions: ({navigation}) => ({
title: 'Aanmelden',
params: {
nuke: navigation.state.params && !!navigation.state.params.nuke,
},
}),
},
Main: {
screen: DynamicTabBar,
navigationOptions: ({navigation}) => ({
title: 'Follow-up',
}),
},
}, {
mode: 'modal',
headerMode: 'none',
});
export class AppWithNavigationState extends React.Component {
constructor(props) {
super(props);
}
render() {
return <AppNavigator navigation={addNavigationHelpers({ dispatch: this.props.dispatch, state: this.props.navigationReducer })} />
}
}
AppWithNavigationState.propTypes = {
dispatch: React.PropTypes.func.isRequired,
navigationReducer: React.PropTypes.object.isRequired,
};
const mapStateToProps = state => ({
navigationReducer: state.navigationReducer,
});
export default connect(mapStateToProps)(AppWithNavigationState);
So far so good, it's just that the DynamicTabBar
is should not be 'rendered' until the user has logged in (i.e. navigating from Login
to Main
).
Here's why
const Tabs = TabNavigator({
Start: {
screen: UserStackNavigator,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="users"
color="#dddddd"
size={20}
/>),
},
},
...Account.User.CanEnter ? {
ConditionalTab: {
screen: ConditionalScreen,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="recycle"
color="#dddddd"
size={20}
/>),
},
}} : {},
Settings: {
screen: Settings,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="cog"
color="#dddddd"
/>),
}
}
},{
...TabNavigator.Presets.AndroidTopTabs,
tabBarPosition: "bottom",
tabBarOptions: {
activeTintColor: '#eaeb65',
showIcon: true,
showLabel: false,
style: { backgroundColor: '#333' },
}
});
export default class DynamicTabBar extends React.Component {
constructor(props) {
super(props);
}
render() {
return <Tabs navigation={this.props.navigation} />;
}
}
DynamicTabBar.router = Tabs.router;
...Account.User.CanEnter ? {
is always false because the TabNavigator
is rendered before the user has logged in and Account
is filled. A failed attempt, it seams.
I wanted to populate Tabs
inside componentWillMount
, but then I can't set the static router: DynamicTabBar.router = Tabs.router;
Any ideas on how to fix this?
Upvotes: 11
Views: 15425
Reputation: 1184
I know this is old, but AFAICT there is no blessed way to do this still, in 2019. Here is the solution I came up with, and I think it's fairly simple.
Basically, I specify a custom tabBarComponent
with a custom getButtonComponent
method that either renders the button or not based on the current user. If the user shouldn't see the nav item, then it renders an empty view, which has the effect of hiding the navigation item.
import { createBottomTabNavigator } from 'react-navigation'
import { BottomTabBar } from 'react-navigation-tabs'
class TabBar extends React.Component {
render() {
return (
<BottomTabBar
{...this.props}
getButtonComponent={this.getButtonComponent}
/>
)
}
getButtonComponent({ route }) {
if (route.key === 'Other') {
return () => <View /> // a view that doesn't render its children
} else {
return null // use the default nav button component
}
}
}
const AppNav = createBottomTabNavigator(
{
Home: HomeStack,
Other: OtherScreen,
},
{
tabBarComponent: TabBar,
}
)
Upvotes: 6
Reputation: 610
Here is my solution. I'm using expo. I needed dynamic amount and content of tabs, which I'm going to get from API.
MainTabNavigator.js
import React from 'react';
import { Platform } from 'react-native';
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
import TabBarIcon from '../components/TabBarIcon';
import SettingsScreen from '../screens/SettingsScreen';
import {getBrowerScreen} from '../screens/BrowserScreen';
const SettingsStack = createStackNavigator({
Settings: SettingsScreen,
});
SettingsStack.navigationOptions = {
tabBarLabel: 'Settings',
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={Platform.OS === 'ios' ? 'ios-options' : 'md-options'}
/>
),
};
// Tab params that we will get from API
var tabParams = [
{title: 'Tab 1', url: 'https://mycar.city', icon: 'md-pricetags'},
{title: 'Tab 2', url: 'https://mycar.city', icon: 'ios-play-circle'},
{title: 'Tab 3', url: 'https://mycar.city', icon: 'ios-phone-portrait'},
{title: 'Tab 4', url: 'https://global-trend.info', icon: 'ios-phone-portrait'},
],
tabs = {};
for (var i=0; i<tabParams.length; i++) {
const tab = tabParams[i];
tabs['Tab' + (i + 1)] = createStackNavigator({
Links: getBrowerScreen(tab.url),
});
tabs['Tab' + (i + 1)].navigationOptions = {
tabBarLabel: tab.title,
tabBarIcon: ({ focused }) => (
<TabBarIcon
focused={focused}
name={tab.icon}
/>
),
};
}
//Adding another kind of tab
tabs.SettingsStack = SettingsStack;
export default createBottomTabNavigator(tabs);
BrowserScreen.js
import React from 'react';
import { View, StyleSheet, WebView } from 'react-native';
import { ExpoLinksView } from '@expo/samples';
export function getBrowerScreen(url) {
return class BrowserScreen extends React.Component {
static navigationOptions = {
header: null,
};
render() {
return (
<View style={styles.container}>
<WebView
source={{ uri: url }}
style={{ width: '100%' }}
/>
</View>
);
}
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
Upvotes: 0
Reputation: 4803
Yessssss! I think I found a way. The solution probably isn't following the design pattern, but this is what I came up with:
export default class DynamicTabBar extends React.Component {
static router = TabRouter({
Start: {
screen: UserStackNavigator,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="users"
color="#dddddd"
size={20}
/>),
},
},
...Account.User.CanEnter ? {
ConditionalTab: {
screen: ConditionalScreen,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="recycle"
color="#dddddd"
size={20}
/>),
},
}} : {},
Settings: {
screen: Settings,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="cog"
color="#dddddd"
/>),
}
}
},{
...TabNavigator.Presets.AndroidTopTabs,
tabBarPosition: "bottom",
tabBarOptions: {
activeTintColor: '#eaeb65',
showIcon: true,
showLabel: false,
style: { backgroundColor: '#333' },
}
});
constructor(props) {
super(props);
}
render() {
const tabs = TabNavigator({
Start: {
screen: UserStackNavigator,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="users"
color="#dddddd"
size={20}
/>),
},
},
...Account.User.CanEnter ? {
ConditionalTab: {
screen: ConditionalScreen,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="recycle"
color="#dddddd"
size={20}
/>),
},
}} : {},
Settings: {
screen: Settings,
navigationOptions: {
tabBarIcon: (<Icon
type="font-awesome"
name="cog"
color="#dddddd"
/>),
}
}
},{
...TabNavigator.Presets.AndroidTopTabs,
tabBarPosition: "bottom",
tabBarOptions: {
activeTintColor: '#eaeb65',
showIcon: true,
showLabel: false,
style: { backgroundColor: '#333' },
}
});
return <Tabs navigation={this.props.navigation} />;
}
}
The router is assigned in a static way and is built dynamically at runtime.
Upvotes: 5