cant send params via navigation navigate - react-native

i want to send parameter name from login screen to home screen via router but data object not defined
TypeError: undefined is not object (evaluating
'navigation.state.params.name'
login screen
<TouchableOpacity
style={styles.buttonContainer}
onPress={() => this.props.navigation.navigate('Home', { name: 'Erry' })}>
<Text style={styles.buttonText}>MASUK</Text>
</TouchableOpacity>
my router
export const LoginStack = SwitchNavigator({
Login: {
screen: Login,
},
Home: {
screen: HomeStack
}
}, {
headerMode: 'none',
navigationOptions: {
headerVisible: false,
}
});
export const HomeStack = StackNavigator({
Home: {
screen: Home
}
});
Home Screen
static navigationOptions = ({navigation}) => ({
title: `${navigation.state.params.name}`,
headerStyle : {
backgroundColor: '#f39c12',
},
headerTitleStyle :{
color: '#353b48',
},
});
any idea ?

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.
You'd better not use SwitchNavigator, you can see the correct example.
And you'd better judge the params is empty or not:
const params = navigation.state.params || {};
const title = params.name || 'default title';

Related

How to combine dynamic routes and static routes in react native

I'm creating a react native wizard, and I want to combine my static route and wizard dynamic routes.
Currently I am using react navigation, and the major problem is i have a generateNavigator function that creates the dynamic routes based on the props passed down to Route.js component where the function exists. I have tried moving the function to the AppNavigator where the static routes exists but am getting navigator must be a function. I'm running the app expo
Route.js
generateNavigator = () => {
const { steps, title } = this.props;
const navigationRoutes = {};
steps.forEach((step, index) => {
const routeOptions = { screen: step.component };
navigationRoutes[step.routeName] = routeOptions;
});
const navigationOptions = {
headerStyle: {
backgroundColor: '#ffffff',
paddingHorizontal: 5,
},
headerTintColor: '#111111',
headerTitle: title,
};
return createAppContainer(createStackNavigator(navigationRoutes, { navigationOptions }));
}
AppNavigator.js
const MainStack = createStackNavigator(
{
Main: {
screen: {
MainTabNavigator,
},
},
...ModalStack
},
{
mode: 'modal',
headerMode: 'none',
}
);
export default createAppContainer(
createSwitchNavigator({
AuthLoading: AuthLoading,
Main: MainStack,
Auth: AuthStack,
}, {
initialRouteName: 'AuthLoading',
})
);
I expect the app to run without warning, but instead I'm getting a warning 'You should only render one navigator explicitly in your app'. As said earlier i tried moving the generateNavigator() to AppNavigator in order to solve the problem but with no success.

Change Bottom Tab Bar based on state in React Navigation with navigationOptions

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.

Call child react navigation prop from super component in react-native / react-navigation

I have a react native application using react navigation. I have following structure in my application.
class MainContainer extends Component {
render() {
return (
<View style={{ flex: 1 }}>
<Header
backgroundColor={appcolors.primaryColor}
leftComponent={<TouchableOpacity onPress={() => this.props.navigation.toggleDrawer() }><Feather name='align-justify' size={24} color='white' /></TouchableOpacity>}
centerComponent={{ text: this.props.headerTitle , style: { color: 'white' } }}
/>
<MainDrawerNavigation/>
</View>
);
}
};
and <MainDrawerNavigation/> is a react navigation component as follows.
MainDrawerNavigation = createDrawerNavigator({
XScreen: {
screen: XScreen,
},
YScreen: {
screen: YScreen,
},
ZScreen: {
screen: ZScreen,
},
},{
}
});
I have got error when trying to call this.props.navigation.toggleDrawer() from MainContainer. Then for testing purpose I have add a button to XScreen and tried to toggle drawer and it was success. So I want to know is there any way to pass child navigation props to super view. So I could call this.props.navigation.toggleDrawer() method and control drawer from MainContainer. or any navigation practices that can be use to solve this.
PS: the error I got is _this2.props.navigation.toggleDrawer is not a function
In MainContainer, the this.props.navigation is not initiated, so you got such error. You can only access it inside the child component of MainDrawerNavigation i.e. XScreen, YScreen & ZScreen only.
For best practices you need to design how all component will navigate into your app and pass Navigator objects/components as root component.
There are multiple navigators, you will can read them react navigation api doc.
A simplified app needs SwitchNavigator, DrawerNavigator, TabBarNavigator & StackNavigator.
SwitchNavigator :
It is used for user authentication. It will give control to toggle between two component (FYI, component can be a Navigator or React.Component).
export const AuthNavigator = SwitchNavigator(
{
AuthLoading: { screen: AuthLoadingScreen },
App: { screen: AppDrawer},
Auth: { screen: AuthStack}
},
{
initialRouteName: 'AuthLoading',
}
);
for more details
DrawerNavigator :
It is used to display side panel or sliding view at left or right side of the screen. So, you can directly pass this component to SwitchNavigator's authentication successful screen.
export default const AppDrawer = DrawerNavigator(
{
Home: { screen: TabBarNav },
Notes: { screen: NotesStack },
Invite: { screen: InviteContactsStack },
Files: { screen: FilesStack },
Settings: { screen: SettingsStack }
},
{
initialRouteName: "Home",
contentOptions: {
activeTintColor: "#e91e63"
},
contentComponent: props => <LeftSidePanel {...props} />
}
);
TabBarNavigator :
It is used to display tab bar at bottom for iOS and at top for android by default. This can be customized.
export default const TabBarNav = TabNavigator(
{
ChatsTab: {
screen: ChatsStack,
navigationOptions: {
tabBarLabel: "Chats"
}
},
InviteContacts: {
screen: InviteContactsStack,
navigationOptions: {
tabBarLabel: "Invite"
}
},
Notifications: {
screen: NotificationsStack,
navigationOptions: {
tabBarLabel: "Notifications"
}
},
Tasks: {
screen: TasksStack,
navigationOptions: {
tabBarLabel: "Tasks"
}
}
},
{
tabBarPosition: "bottom",
}
);
StackNavigator :
It's name suggests that it will hold a stack of component. So, when any item is selected from drawer, you can directly put a StackNavigator in that screen.
export default const ChatsStack = StackNavigator({
Chats: {
screen: Chats
},
Messages: {
screen: Messages
}
});
You can read Spencer Carli's blog on medium which explain with code.
Please let me know whether it satisfies your need.

React navigation mix navigation

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

React native TabNavigator navigation params

I want to send route params to Profile tab from button click action in Home screen. Below code is tab navigator
const Tabs = TabNavigator({
[routes.HOME]: {
screen: FeedMainScreen,
navigationOptions: {
tabBarLabel: tabBarLabels.HOME_TAB_LABEL,
},
},
[routes.SEARCH]: {
screen: SearchScreen,
navigationOptions: {
tabBarLabel: tabBarLabels.SEARCH_TAB_LABEL,
},
},
[routes.PROFILE]: {
screen: ProfileScreen,
navigationOptions: {
tabBarLabel: tabBarLabels.PROFILE_TAB_LABEL,
},
},
}
and button click navigation is like this
<Button onPress={() => this.props.navigation.navigate(routes.PROFILE, { id: this.props.user._id }, null)} />
and accsess param from Profile screen like this
constructor (props) {
super(props)
console.log('NavPrams', this.props.navigation.state)
}
alwaya undifined
You can use connect() method to successfully map the props and use them anywhere.
Add the below mentioned code in the class where is written -
const mapStateToProps = state => {
return {
user: this.props.user;
};
};
export default connect(mapStateToProps, {})(className);