Cannot read property 'navigation' of undefined - react-native

I'm trying to navigate with NavigationActions, but for some reason it won't navigate.
I'm trying to move from LoginScreen to HomeScreen.
loginscreen:
constructor(props){
super(props)
this.state = {
email: '',
password: '',
status: '',
}
this.handlePress = this.handlePress.bind(this)
}
handlePress(){
firebaseRef.auth().signInWithEmailAndPassword(this.state.email, this.state.password).then(function(firebaseUser){
//Success, move to homepage.
const resetAction = NavigationActions.reset({
index: 0,
actions: [
NavigationActions.navigate({ routeName: 'Home'})
]
})
this.props.navigation.dispatch(resetAction) <-- SAYS NAVIGATION IS UNDEFINED
}).catch(function(error){
//Failed to log in, print error.
console.log(error)
});
}
this is where I call handlePress:
<TouchableOpacity style={styles.logBtn} onPress={()=>this.handlePress()}>
<Text style={styles.logTxt}>
Login
</Text>
</TouchableOpacity>
This is 'navigation'(in render method, loginscreen):
const { navigation } = this.props.navigation;
this is app.js where I set the navigation:
import React from 'react';
import { AppRegistry } from 'react-native';
import { StackNavigator, TabNavigator } from 'react-navigation';
import Ionicons from 'react-native-vector-icons/Ionicons';
import LoginScreen from './app/screens/LoginScreen';
import RegisterScreen from './app/screens/RegisterScreen';
import HomeScreen from './app/screens/HomeScreen';
import FriendsScreen from './app/screens/FriendsScreen';
const Stylelist = StackNavigator({
Login:{
screen: LoginScreen,
navigationOptions: ({navigation}) =>({
header: null,
}),
},
Register:{
screen: RegisterScreen,
navigationOptions: ({navigation}) =>({
header: null,
}),
},
Home:{
screen: TabNavigator({
Home: {
screen: HomeScreen,
navigationOptions: ({ navigation }) => ({
title: 'Home',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-home' : 'ios-home-outline'}
size={26}
style={{ color: tintColor }}
/>
)
}),
},
Friends: {
screen: FriendsScreen,
navigationOptions: ({ navigation }) => ({
title: 'Friends',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-people' : 'ios-people-outline'}
size={26}
style={{ color: tintColor }}
/>
)
}),
},
}),
navigationOptions: ({ navigation }) => ({
title: 'Home',
headerStyle: {backgroundColor: "#553A91"},
headerTitleStyle: {color: "#FFFFFF"},
}),
}
});
export default Stylelist;
I tried to log the error from the 'catch' and I get this error:
Cannot read property 'navigation' of undefined
How can I fix it?

It should be
const { navigate} = this.props.navigation;
or
const { navigation } = this.props;
Actually, what you get in props is navigation object, which has navigate method in it.And what you were doing was
const { navigation } = this.props.navigation;
This gets evaluates to
this.props.navigation.navigation
It says look for navigation property inside navigation object, which actually is not present, hence giving undefined. I hope this clears your doubt.

I had this problem too.
Of course I had used setTimeout. There may be someone who has this problem, so it's a good reminder.
only use let that = this;
componentDidMount() {
let that = this ;
setTimeout(function () {
let toHome = StackActions.reset({
index: 0,
actions: [NavigationActions.navigate({routeName: 'MainScreen'})]
});
that.props.navigation.dispatch(toHome);
},3000);
}

Make sure your function is bind with this.
onPress={()=>this.handlePress()}
instead
onPress={()=>this.handlePress.bind(this)}

Related

How to properly stack nested navigators within an App Container?

I currently have an AppContainer which consists of a bottom tab navigator and currently one more navigation set for Profile settings. I am trying to understand how to properly arrange everything, specifically how to nest navigation in one tab Profile so I can access two other pages QrCode and EditAccount each through two buttons. How do I achieve the below output? I believe I have to nest profile navigation somehow.
MY TABS BELOW
Home
Queue
Profile
:in here i have two other pages i can access QRCode and EditAccounts
App.js
const bottomTabNavigator = createBottomTabNavigator(
{
Home: {
screen: Home,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="home" size={25} color={tintColor} />
)
}
},
Queue: {
screen: Queue,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="comments" size={25} color={tintColor} />
)
}
},
Profile: {
screen: Profile,
navigationOptions: {
tabBarIcon: ({ tintColor }) => (
<Icon name="user" size={25} color={tintColor} />
)
}
}
})
const navigationOptions = ({ navigation }) => {
return {
headerTitle: 'hello',
headerStyle: {
height: '45%',
backgroundColor: 'black'
},
headerTintColor: 'white',
headerLeft: (
<TouchableWithoutFeedback
style={{ /* Put your style here */}}
onPress={() => navigation.goBack()} >
<Ionicons name="ios-arrow-dropleft" size={32} color="white" />
</TouchableWithoutFeedback>
)
}
}
const ProfileNavigator = createStackNavigator({
//Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
Profile: { screen: Profile },
EditAccount: { screen: EditAccount, navigationOptions }
});
const AppNavigator = createSwitchNavigator({
tabs: bottomTabNavigator,
profile: ProfileNavigator
})
const AppContainer = createAppContainer(AppNavigator);
After Applying Answer
The solution offers works as needed with the code below:
const navigationOptions = ({ navigation }) => {
return {
headerTitle: 'hello',
headerStyle: {
height: '45%',
backgroundColor: 'black'
},
headerTintColor: 'white',
headerLeft: (
<TouchableWithoutFeedback
style={{ /* Put your style here */}}
onPress={() => navigation.goBack()} >
<Ionicons name="ios-arrow-dropleft" size={32} color="white" />
</TouchableWithoutFeedback>
)
}
}
const ProfileNavigator = createStackNavigator({
Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
});
//ADDED
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};
const AppNavigator = createSwitchNavigator({
tabs: bottomTabNavigator,
profile: ProfileNavigator
})
const AppContainer = createAppContainer(AppNavigator);
The only issue is I am unsure how to port over the header options to the tabs in the in the bottomTabNavigator. I have a custom component in place for the profile page that makes it look like this (black bar with button icon for Profile):
I can then navigate to the EditAccounts by pressing on the user icon. But when I navigate back from EditAccounts back to profile, the page renders with the navigationOptions header like so:
How do I apply this properly so I can lean simply on the navigationOptions header and push a custom name onto it (get rid of my custom component in that case)?
There are 2 cases.
If you want to keep tabs when accessing QR or EditAccount
const BottomTabNavigator = createBottomTabNavigator({
...
Profile: {
screen: ProfileNavigator,
}
})
const ProfileNavigator = createStackNavigator({
Profile: { screen: Profile},
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
});
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};
const AppContainer = createAppContainer(BottomTabNavigator);
If you don't want to keep tabs
const BottomTabNavigator = createBottomTabNavigator({
...
Profile: {
screen: Profile,
}
})
const AppNavigator = createStackNavigator({
Tabs: BottomTabNavigator,
QR: { screen: GenerateQR, navigationOptions },
EditAccount: { screen: EditAccount, navigationOptions }
})
const AppContainer = createAppContainer(AppNavigator);
UPDATED:
Add navigationOptions to ProfileNavigator to remove header
ProfileNavigator.navigationOptions = () => {
const navigationOptions = {
header: null,
headerMode: 'none',
};
return navigationOptions;
};

react navigation - DrawerNavigator for Header Menu icon inside TabNavigator-> StackNavigator

I want to display HeaderLeft Menu icon globally in all screens. When I click on Menu Icon, I need to display Drawer Menu. I use "OpenDrawer", "CloseDrawer" methods for open/close drawer menu.
But I am always getting "undefined is not a function (evaluating 'props.navigation.openDrawer()')". I also tried the following
props.navigation.dispatch(DrawerActions.openDrawer())
props.navigation.navigate(openDrawer())
But None of the above worked. Here is my partial code
const MenuButton = (props) => {
return (
<View>
<TouchableOpacity onPress={() => { props.navigation.dispatch(DrawerActions.openDrawer())} }>
<Text>Menu</Text>
</TouchableOpacity>
</View>
)
};
const MyDrawerNavigator = createDrawerNavigator(
{
Wishist: {
screen: wishlist
},
},
{
contentComponent: SideMenuScreen,
drawerWidth: 200,
}
);
const AppNavigator = createBottomTabNavigator({
Home: {
screen: createStackNavigator({
Home: {
screen: Home
},
Contact: {
screen: Contact
}
},
{
defaultNavigationOptions: ({ navigation }) => ({
headerStyle: {
backgroundColor: 'white',
borderWidth:0,
borderBottomWidth:0
},
headerTitle: headerTitleNavigationOptions('HOME'),
headerLeft: <MenuButton navigation={navigation}/>,
headerRight: headerRightNavigatorOptions(navigation)
})
}),
navigationOptions: ({ navigation }) => ({
headerStyle: {
backgroundColor: 'white',
borderWidth:0,
borderBottomWidth:0
},
}),
}},
{
tabBarOptions: {
showLabel: false,
style: {
backgroundColor: 'white',
borderTopWidth:1
}
},
initialRouteName: 'Home',
tabBarComponent: TabBarBottom,
tabBarPosition: 'bottom',
animationEnabled: false,
swipeEnabled: false
}
);
const App = createAppContainer(AppNavigator);
you need to import the DrawerActions in your component from react-navigation-drawer as explained in the docs
DrawerActions is an object containing methods for generating actions specific to drawer-based navigators. Its methods expand upon the actions available in NavigationActions.
The following actions are supported:
openDrawer - open the drawer
closeDrawer - close the drawer
toggleDrawer - toggle the state, ie. switche from closed to open and
vice versa
import { DrawerActions } from 'react-navigation-drawer';
this.props.navigation.dispatch(DrawerActions.openDrawer())
The react-navigation api do not provide more information, but you can consult the NavigationActions api reference for more information.
NavigationActions reference
All NavigationActions return an object that can be sent to the router using navigation.dispatch() method.
Note that if you want to dispatch react-navigation actions you should use the action creators provided in this library.
You need to import NavigationActions in your component and then you can dispatch your action with something like this.props.navigation.dispatch(navigateAction);
import { NavigationActions } from 'react-navigation';
const navigateAction = NavigationActions.navigate({
routeName: 'Profile',
params: {},
action: NavigationActions.navigate({ routeName: 'SubProfileRoute' }),
});
this.props.navigation.dispatch(navigateAction);
also as explained from Ashwin Mothilal, make sure you are passing the navigation prop inside your component. For example you can run a console.warn(props) and immediatly see the result on the emulator.
This is your component:
import { DrawerActions } from 'react-navigation-drawer';
const MenuButton = (props) => {
return (
<View>
<TouchableOpacity onPress={() => {
console.warn(props);
props.navigation.dispatch(DrawerActions.openDrawer());
}}>
<Text>Menu</Text>
</TouchableOpacity>
</View>
)
};
At first, just console the props in Menu button and check if you get openDrawer or closeDrawer or any other method you are looking for then you can call it.

react navigation: change tabNavigator style based on redux store

So I'm trying to style the tabNavigator based on the store state:
import React from 'react';
import { connect } from 'react-redux';
import { TabNavigator } from 'react-navigation';
const Tabs = TabNavigator(
// Screens
{
Boards: {
screen: Boards,
navigationOptions: {
tabBarLabel: 'Boards',
tabBarIcon: () => <MaterialCommunityIcon name="bulletin-board" size={25} color="white" />,
},
},
Bookmarks: {
screen: Bookmarks,
navigationOptions: {
tabBarLabel: 'Bookmarks',
tabBarIcon: () => <EntypoIcon name="bookmarks" size={25} color="white" />,
},
},
Settings: {
screen: Settings,
navigationOptions: {
tabBarLabel: 'Settings',
tabBarIcon: () => <MaterialCommunityIcon name="settings" size={25} color="white" />,
},
},
},
// TabNavigator configuration
{
tabBarPosition: 'bottom',
tabBarOptions: {
showIcon: true,
showLabel: false,
renderIndicator: () => null,
style: {
// TODO: Make tabNavigation color change based on current selected theme.
backgroundColor: this.props.theme === 'default' ? 'black' : 'red',
},
},
},
);
const mapStateToProps = state => {
return {
theme: state.configuration.theme,
};
};
export default connect(mapStateToProps)(Tabs);
But when I try to use this.props.theme I get: undefined is not an object (evaluating 'this.props.theme') I guess this happens because tabNavigator doesn't accept props or something like that, so I can't connect tabNavigator to the redux store, so how can I implement what I'm trying to do?
Edit 1
After trying to resolve this using a custom tab bar in the way that was suggested above, this error pops up:
And the code:
TabBar.js
import React from 'react';
import { connect } from 'react-redux';
import { TabBarBottom } from 'react-navigation';
const TabBar = ({ theme }) => (
<TabBarBottom style={{ backgroundColor: theme === 'dark' ? 'black' : 'red' }} />
);
const mapStateToProps = state => ({
theme: state.configuration.theme,
});
export default connect(mapStateToProps)(TabBar);
router.js
import { TabNavigator } from 'react-navigation';
import TabBar from './components/TabBar';
import Boards from './screens/Boards';
import Settings from './screens/Settings';
import Bookmarks from './screens/Bookmarks';
const Tabs = TabNavigator(
// Screens
{
Boards: {
screen: Boards,
navigationOptions: {
tabBarLabel: 'Boards',
tabBarIcon: () => <MaterialCommunityIcon name="bulletin-board" size={25} color="white" />,
},
},
Bookmarks: {
screen: Bookmarks,
navigationOptions: {
tabBarLabel: 'Bookmarks',
tabBarIcon: () => <EntypoIcon name="bookmarks" size={25} color="white" />,
},
},
Settings: {
screen: Settings,
navigationOptions: {
tabBarLabel: 'Settings',
tabBarIcon: () => <MaterialCommunityIcon name="settings" size={25} color="white" />,
},
},
},
// TabNavigator configuration
{
tabBarPosition: 'bottom',
tabBarComponent: TabBar,
},
);
export default Tabs;
You can create your own tab bar, hook it up to the navigator and to redux.
const MyAwesomeTabBar = ({theme}) => (
<View>
...
</View>
)
export default connect(mapStateToProps)(MyAwesomeTabBar);
And then in your navigator definition:
const Tabs = TabNavigator(
// Screens
{
...
},
// TabNavigator configuration
{
tabBarPosition: 'bottom',
tabBarComponent: MyConnectedAwesomeTabBar
},
);
As for the separation of presentational/functional components - I think that it's not so much that not doing it is bad practice, as much as doing it is good practice. And, you can pretty easily separate it here as well, just have MyAwesomeTabBar be your functional component, which uses a bunch of presentational ones.

How to avoid overriding 'title' of the header in TabNavigator and its children screens? (react-navigation)

I want to set the title of the TabNavigator as 'Stylebook' with a button.
However, the screens which are children of TabNavigator change the 'title'. I think it's overriding the 'title' prop. How can I avoid it?
This is TabNavigator (my TabBar is in the StackNavigator)
export default TabNavigator(
{
Category: {
screen: StylebookCategoryScreen
},
All: {
screen: StylebookAllScreen
}
} ,{
navigationOptions: ({ navigation }) => ({
title: 'Stylebook',
headerRight: (
<Button onPress={() => navigation.navigate('Wardrobe')}
title="Wardrobe"
/>
)
})
});
StylebookAllScreen is almost identical with StylebookCategoryScreen.
class StylebookAllScreen extends Component {
static navigationOptions = {
title:'All'
}
render() {
return (
<View>
<Text>StylebookALLScreen</Text>
<Text>StylebookALLScreen</Text>
</View>
)
}
}
export default StylebookAllScreen;
I can also change whole structure of the navigator, if I can fix this.
Thanks!
You have to put your StylebookAllScreen component into a StackNavigator and then put that into your TabNavigator. Putting it into a StackNavigator allows you to set props like
navigationOptions: ({navigation}) => ({
title: "Stylebook",
}),
With headerLeft or headerRight you can add a Component (such as a Button to the left/right of your headline.
Try something like this:
const StylebookStack = StackNavigator({
StylebookAllScreen: {
screen: Map,
navigationOptions: ({ navigation }) => ({
title: "Stylebook",
headerTitleStyle: {
....
},
}),
},
});
And then put that into your TabNavigator
export default TabNavigator(
{
Category: {
screen: StylebookCategoryScreen
},
All: {
screen: StylebookStack // <--- HERE
}
} ,{
navigationOptions: ({ navigation }) => ({
title: 'Stylebook',
headerRight: (
<Button onPress={() => navigation.navigate('Wardrobe')}
title="Wardrobe"
/>
)
})
});

There is no route defined for key

I'm using react native, with redux and react navigation. I have everything setup, however, I'm getting a weird issue/error
In my reducer, I have the following
const initialNavState = {
index: 1,
routes: [
{ key: 'Login', routeName: 'Login' },
{ key: 'ResetPassword', routeName: 'ResetPassword' },
],
};
const initialAuthState = { isLoggedIn: false };
const AppReducer = combineReducers({
nav: (state = initialNavState, action) => {
switch(action.type) {
case 'Login':
return AppNavigator.router.getStateForAction(NavigationActions.back(), state);
case 'Logout':
return AppNavigator.router.getStateForAction(NavigationActions.navigate({ routeName: 'Login' }), state);
case 'ResetPassword':
return AppNavigator.router.getStateForAction(NavigationActions.navigate({ routeName: 'ResetPassword' }), state);
case 'Home':
return MainScreenNavigator.router.getStateForAction(NavigationActions.navigate({ routeName: 'Home' }), state);
default:
return AppNavigator.router.getStateForAction(action, state);
}
}
});
export default AppReducer;
This works fine and I can navigate better my two views but when I add another route
{ key: 'Home', routeName: 'Home' },
I get the following error "There is no route defined for key must by one of login resetpassword.
Not sure what I'm doing wrong?
Updated my router
// Native tab navigation
export const TabsNavigation = TabNavigator({
Home: {
screen: Home,
navigationOptions: ({ navigation }) => ({
tabBarLabel: 'Home',
headerLeft : <DrawerIcon navigation={navigation} />,
tabBarIcon: ({ tintColor }) => <Icon name="home" size={28} color={tintColor} type={"font-awesome"} />
}),
},
Photos: {
screen: Photos,
navigationOptions: {
tabBarLabel: 'Photos',
tabBarIcon: ({ tintColor }) => <Icon name="camera" size={28} color={tintColor} type={"font-awesome"} />
},
},
SiteDiary: {
screen: SiteDiary,
navigationOptions: {
tabBarLabel: 'Site Diary',
tabBarIcon: ({ tintColor }) => <Icon name="cog" size={28} color={tintColor} type={"font-awesome"} />
},
},
});
export const AppNavigator = StackNavigator({
Login: {
screen: Login,
navigationOptions: {
title: "Login",
headerLeft: null
},
initialRouteName : 'Home'
},
ResetPassword: {
screen: ResetPassword,
navigationOptions: {
title: "Reset Password",
headerLeft: null
},
}
});
// Main navigation StackNavigator
export const MainScreenNavigator = StackNavigator({
Home: {
screen: TabsNavigation,
navigationOptions: {
title: "Home",
headerLeft: null,
}
}
});
Index.js (main)
// App state
const AppWithNavigationState = connect( state => ({ nav: state.nav})) (({ dispatch, nav }) => (
<AppNavigator navigation = { addNavigationHelpers ({ dispatch, state: nav })} />
));
class App extends React.Component {
store = createStore(AppReducer, undefined, autoRehydrate());
componentDidMount() {
persistStore(this.store, { storage: AsyncStorage });
}
render() {
return (
<Provider store={this.store}>
<AppWithNavigationState />
</Provider>
);
}
}
AppRegistry.registerComponent('App', () => App);
export default App;
I the core issue is with AppNavigator on my main file, I'm trying to have
AppNavigator -- this holds all the public login views
MainScreenNavigator -- This is the main app
and later add a darwer but right now would be nice to get just this working.
The issue is I'm not sure how you can jump between StackNavigator