React native navigation: '_this2.props.navigation.navigate' - react-native

my App.js code
import React from 'react'
import { Icon } from 'react-native-elements'
import { StyleSheet, View, StatusBar } from 'react-native'
import { createAppContainer } from 'react-navigation'
import { createStackNavigator } from 'react-navigation-stack'
import HomeScreen from './screens/homeScreen'
import LoginScreen from './screens/loginScreen'
import SignupScreen from './screens/signup'
import Screen2 from './screens/screen2'
import Screen1 from './screens/screen1'
export default class App extends React.Component {
setLogged(){
this.setState({logged:true})
this.forceUpdate()
}
//change to false when want to enable login feature else true
state = {
logged: false,
}
render() {
if(this.state.logged){
return(
<View style={styles.container} >
<StatusBar hidden = {false}/>
<AppContainer />
</View>
)
}else{
return(
<View style={styles.container} >
<StatusBar hidden = {false}/>
<LoginScreen signup={()=>this.props.navigation.navigate('Signup')} success={()=>this.setLogged()} />
</View>
)
}
}
}
const styles = StyleSheet.create({
container: {
position: 'relative',
width: '100%',
height: '100%',
backgroundColor: '#000000',
},
})
const HomeAppContainer = createStackNavigator(
{
Home: {screen: HomeScreen},
Signup: { screen: SignupScreen },
Screen1: { screen: Screen1 },
Screen2: { screen: Screen2 },
},
{initialRouteName: "Home"},
);
const AppContainer = createAppContainer(HomeAppContainer)
and the login screen contains
import React, { Component } from 'react';
import { StyleSheet, View, Text, TouchableOpacity, TextInput, ToastAndroid } from 'react-native';
import Colors from '../constants/colors'
import GLOBAL from '../constants/global'
export default class LoginScreen extends Component {
state = {
email: '',
password: '',
}
login() {
if (userid == 'admin') {
ToastAndroid.show('Invalid email/password', ToastAndroid.SHORT);
} else {
GLOBAL.userid = userid;
this.props.success()
}
})
}
render() {
return (
<View style={styles.MainContainer}>
<Text style={styles.text}>Email:</Text>
<View style={styles.inputView}>
<TextInput
style={styles.inputs}
autoFocus
returnKeyType="next"
keyboardType="email-address"
onChangeText={(email) => { this.setState({ email: email }) }}
/>
</View>
<Text style={styles.text}>Password:</Text>
<View style={styles.inputView}>
<TextInput
style={styles.inputs}
secureTextEntry
onChangeText={(password) => { this.setState({password:password}) }}
/>
</View>
<View style={styles.buttonGroup}>
<TouchableOpacity
style={styles.button}
onPress={() => { this.login() }} >
<Text style={{ fontSize: 24 }}>Sign in</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.button}
onPress={() => { this.props.signup() }}>
<Text style={{ fontSize:24 }}>Sign up</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
//my styles here
});
the error is as follows
TypeError: undefined is not an object(evaluating '_this2.props.navigation.navigate')
i am learning react-native and making this login screen.
The app will check if the user is already logged in. if not then he will be prompted login screen which will have sign up option.
If the user is already logged in the app will directly go to home screen where screen1 and screen2 is used in stack

Well, the navigation prop is only passed down the navigation tree. Your App component is not a navigation screen like Home/Signup... (it doesn't have a route)... So basically App is not part of a navigator, it's not created using createStackNavigator or the others.
So, in conclusion the navigation prop is not defined.
Basically, how to fix this: LoginScreen should also be a route inside a navigator, therefore the navigation prop will be defined. Your App component should not render anything besides the <AppContainer /> and further logic should be handled in the first screen defined in the routes, which in your case might be the LoginScreen. In there you'll check if the user is logged in or not and navigate accordingly.
There's a great guide on their website on how to accomplish authentication flow: https://reactnavigation.org/docs/en/4.x/auth-flow.html

Related

React Native Redux - Not showing updated state via different route

I'm writing a react-native app via expo and trying to implement redux. I'm not sure if I'm going about this completely the wrong way. I have a home page that has two sections, a search text box and an area with links to content pages. The content pages also contain the same search box component. I want to be able to pass the contents of the search box input to the content page so that the user doesn't need to enter this again (and will probably require access to this content further in the user journey)
My app.js looks like the below:
import React from 'react';
import 'react-native-gesture-handler';
import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from 'react-navigation';
import ContentPage from './pages/ContentPage.js';
import LogoTitle from './components/LogoTitle';
import EventsListPage from './pages/EventsListPage.js';
import EventPage from './pages/EventPage';
import VenuePage from './pages/VenuePage';
import HomeScreen from './pages/HomePage';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
const initialState ={
postcode:"abcefg"
}
const reducer = (state = initialState, action) => {
switch(action.type)
{
case 'SET_POSTCODE':
return {
postcode: action.text
}
default:
console.log("returning default state")
return state
}
}
const store = createStore(reducer);
const RootStack = createStackNavigator(
{
Home: HomeScreen,
ContentPage: ContentPage,
EventsListPage: EventsListPage,
EventPage: EventPage,
VenuePage: VenuePage
},
{
initialRouteName: 'Home',
defaultNavigationOptions: {
headerTitle: () => <LogoTitle />,
headerLeft: () => null,
headerStyle: {
backgroundColor: '#ADD8E6'
}
},
}
);
const AppContainer = createAppContainer(RootStack);
export default class App extends React.Component {
render() {
return (
<Provider store={store}>
<AppContainer />
</Provider>);
}
}
Homepage.js:
import React from 'react';
import { StyleSheet, View } from 'react-native';
import SearchBox from '../components/SearchBox'
import TypeDrillDownArea from '../components/TypeDrillDownArea'
import 'react-native-gesture-handler';
class HomeScreen extends React.Component {
constructor(props) {
super(props);
state = {
};
}
render() {
return (
<View style={styles.container}>
<SearchBox navigation={this.props.navigation} eventTypeId=''/>
<TypeDrillDownArea navigation={this.props.navigation} />
</View>
);
}
}
export default HomeScreen
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'stretch'
},
});
Relevant searchbox.js:
render() {
return (
<ImageBackground source={topperBackground} style={{width: '100%'}}>
<View>
<View style={styles.row}>
<View>
<TextInput
value={this.props.postcode}
autoCapitalize="characters"
style={styles.inputBox}
placeholder="Enter Postcode"
onChangeText={(e) => this.props.setPostcode(e)}
/>
</View>
<View>
<TouchableOpacity
disabled={this.state.locationDisabled}
onPress={() => {
this.props.navigation.navigate('EventsListPage', {
navigation: this.props.navigation,
eventTypeId: this.state.eventTypeId,
locLongitude: this.state.location.coords.longitude,
locLatitude: this.state.location.coords.latitude,
});
}}>
<Image
style={styles.locationPin}
source={locationPin}
/>
</TouchableOpacity>
</View>
</View>
<View style={styles.searchButtonArea}>
<TouchableOpacity
onPress={() => {
console.log("postcode is: " + this.state.postcode)
this.props.navigation.navigate('EventsListPage', {
eventTypeId: this.state.eventTypeId,
postcode: this.props.postcode,
});
}}>
<Text style={styles.searchButton}>SEARCH</Text>
</TouchableOpacity>
</View>
</View>
</ImageBackground>);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(SearchBox)
function mapStateToProps(state){
return {
postcode:state.postcode
}
}
function mapDispatchToProps(dispatch){
return{
setPostcode : (e) => dispatch({
type: 'SET_POSTCODE',
postcode : e
})
}
}
and finally relevant contentpage.js:
<View style={styles.container}>
<SearchBox navigation={this.props.navigation} eventTypeId={this.state.eventTypeId} />
<Image source={imageType(this.state.dataSource[0].eventTypeId)} style={styles.eventType} />
<Text style={styles.textToDisplay}>
{this.state.dataSource[0].eventTypeDescription}
</Text>
</View>
On load, the box is prepopulated with "abcefg" as expected. Changing the contents hits the reducer as expected. However when I navigate to a content page which loads the search box again the value is empty, regardless if I've changed the original state or not.
Am I missusing redux for what it's intended? Should I be doing this a different way?
For clarity below is the organisation of the components
In the reducer(), you are accessing action.text but in the dispatch(), you passed postcode value to postcode instead of text.

undefined is not an object (evaluating'_this2.props.navigation.navigate') - React Native

I am having trouble navigating through screens with a simple button.
This is my App.js
export default class App extends Component<Props> {
render() {
return (
<AppStackNavigator/>
);
}
}
const AppStackNavigator = StackNavigator({
Login: { screen: LoginScreen },
Home: { screen: HomeScreen },
},{
navigationOptions: {
gesturesEnabled:false
}
})
Login.js
export default class Login extends Component {
render() {
return(
<SafeAreaView style={styles.container}>
<StatusBar barStyle='light-content'/>
<KeyboardAvoidingView behavior='padding' style={styles.container}>
<TouchableWithoutFeedback style={styles.container} onPress={Keyboard.dismiss}>
<View style={styles.logoContainer}>
<View style={styles.logoContainer}>
<Image style={styles.logo}
source={require('../images/logo/logo.png')}>
</Image>
<Text style={styles.title}>
Sports Chat App
</Text>
</View>
<View style={styles.infoContainer}>
<TextInput style={styles.input}
placeholder="Enter name"
placeholderTextColor='#ffffff'
keyboardType='default'
returnKeyType='next'
autoCorrect={false}
onSubmitEditing={()=> this.refs.phoneNumber.focus()}
/>
<TextInput style={styles.input}
placeholder="Enter phone number"
placeholderTextColor='#ffffff'
keyboardType='phone-pad'
ref={'phoneNumber'}
/>
<TouchableOpacity style={styles.buttonContainer}>
<Button title='LogIn' onPress={()=>this.props.navigation.navigate('Home')}/>
</TouchableOpacity>
</View>
</View>
</TouchableWithoutFeedback>
</KeyboardAvoidingView>
</SafeAreaView>
)
}
};
LoginScreen.js
class LoginScreen extends Component {
render(){
return (
<View>
<Login>
</Login>
</View>
);
}
}
I keep getting the error undefined is not an object (evaluating'_this2.props.navigation.navigate') whenever i try use the Login button in Login.js,
I want this to navigate to the home screen but cant figure out why this keeps happening.
If i try do it without the components it works fine.
Click to View error message
Add a navigation props to Login in LoginScreen.js, Because LoginScreen has props navigation but Login don't.
render(){
return (
<View>
<Login navigation ={this.props.navigation}>
</Login>
</View>
);
}
App.js
import React, { Component } from "react";
import { Text, View } from "react-native";
import AppStackNavigator from "./AppStackNavigator";
export default class App extends Component<Props> {
render() {
return <AppStackNavigator />;
}
}
AppStackNavigator
import React, { Component } from "react";
import { StackNavigator } from "react-navigation";
import { View, Text, TouchableOpacity } from "react-native";
const LoginScreen = props => (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Login navigation={props.navigation} />
</View>
);
const HomeScreen = () => (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Home</Text>
</View>
);
const Login = props => (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<TouchableOpacity
onPress={() => {
props.navigation.navigate("Home");
}}
>
<Text>GO to home from login</Text>
</TouchableOpacity>
</View>
);
export default (AppStackNavigator = StackNavigator(
{
Login: { screen: LoginScreen },
Home: { screen: HomeScreen }
},
{
headerMode: "none",
navigationOptions: {
gesturesEnabled: false
}
}
));
You need to access the navigation prop in the component. Check the official guide.
reactnavigation Official guide
import React from 'react';
import { Button } from 'react-native';
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />;
}
}
// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);

react-native navigation in Flatlist component over multiple files

I am using react-navigation for navigation and right now i am trying to navigate between Screens using my flatlist. I want it so that when i click on an item in the list that i get send to the Details screen, but whenever i press on an item in the list with this code, nothing happens. I tried to pass the navigation property from the Homescreen component to the MyListItem Component but then i get undefined is not an Object error.
However, i have a Test TouchableOpacity in my Homescreen Component and if i click on that, i can navigate to the Details screen (See "Test" Text in Homescreen Component).
I think i did something wrong with the navigation property, but i have been searching everywhere and have not found a solution.
This is my App.js file with the StackNavigator:
import React from 'react';
import { createStackNavigator } from 'react-navigation'
import HomeScreen from './screens/HomeScreen'
import DetailScreen from './screens/DetailScreen'
const RootStack = createStackNavigator(
{
Home: HomeScreen,
Details: DetailScreen,
},
{
initialRouteName: 'Home',
navigationOptions: {
header: null,
},
}
);
export default class App extends React.Component {
render() {
return <RootStack />;
}
}
This is my HomeScreen file where the Problem is happening:
import React from 'react'
import { StyleSheet, Text, View, TouchableOpacity, StatusBar,
FlatList, Image } from 'react-native'
import Data from '../data/Data'
class MyListItem extends React.Component {
render() {
return(
<View style={styles.container}>
<TouchableOpacity
onPress={this.props.handleOnPress}
>
<View style={{ flexDirection: 'row', heigth: 100, width: 100 }}>
<View>
<Image style={{ height: 50, width: 50, resizeMode: 'contain' }} source={require('../res/icon.png')} />
</View>
<View style={{ justifyContent: 'center' }}>
<Text>
{this.props.item.name}
</Text>
</View>
</View>
</TouchableOpacity>
</View>
);
}
}
class HomeScreen extends React.Component {
handleOnPress = () => {
this.props.navigation.navigate('Details')
}
render() {
return (
<View>
<StatusBar hidden={true} />
<TouchableOpacity
onPress={() => this.props.navigation.navigate('Details')}
>
<Text>Test</Text>
</TouchableOpacity>
<FlatList
data={Data}
renderItem={({ item }) =>
<MyListItem
item={item}
onPress={this.handleOnPress}
/>
}
/>
</View>
);
}
}
export default HomeScreen;
Ps: I run the Code on an Android emulator.
Edit: edited answer suggestion into code
Might be a typo mistake but, you try to navigate to navigate('Details') when you declared your screen as Detail
{
Home: HomeScreen,
Detail: DetailScreen, <----
},

How to change screen in react native with react-navigation on click of a button?

I have defined all my routes/navigations in my root component (App.js) and am trying to access one of those screens (UserScreen) from a child component(LoginScreen) on click of a button.
Here is my App.js
import React from 'react';
import { StyleSheet, Text, View, TextInput, TouchableHighlight, Button } from 'react-native';
import LoginComponent from './components/LoginComponent';
import { StackNavigator } from 'react-navigation';
class MainWelcome extends React.Component {
render() {
return (
<View>
<Text>Welcome Page</Text>
<Button
title="Login"
onPress={() => this.props.navigation.navigate('Login')}
/>
</View>
);
}
}
class LoginScreen extends React.Component {
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<LoginComponent />
</View>
);
}
}
class UserScreen extends React.Component {
render() {
return (
<View>
<Text>Details Screen</Text>
</View>
);
}
}
const RootStack = StackNavigator(
{
Register: {
screen: RegisterScreen,
},
Login: {
screen: LoginScreen,
},
UserPage: {
screen: UserScreen,
},
Welcome: {
screen: MainWelcome,
},
},
{
initialRouteName: 'Welcome',
}
);
export default class App extends React.Component {
render() {
return (
<RootStack />
);
}
}
This is my LoginComponent.js
import React from 'react';
import { StyleSheet, Text, View, TouchableHighlight, TextInput, StackNavigator } from 'react-native';
class LoginComponent extends React.Component {
constructor(){
super();
this.state = {
loginusername: '',
loginpassword: '',
isloggedin: false,
loggedinuser: null,
}
}
render() {
return (
<View>
<Text>Please Log In</Text>
<View>
<TextInput
placeholder="USERNAME"
placeholderTextColor = 'black'
onChangeText={(loginusername) => this.setState({loginusername})}
/>
<TextInput
placeholder="Password"
placeholderTextColor = 'black'
onChangeText={(loginpassword) => this.setState({loginpassword})}
/>
<TouchableHighlight onPress={() => {
{this.props.navigation.navigate('UserPage')}
}
}>
<View>
<Text>Login</Text>
</View>
</TouchableHighlight>
</View>
</View>
);
}
}
export default LoginComponent;
Here in LoginComponent.js, I am doing {this.props.navigation.navigate('UserPage')} to redirect to the userscreen on click of a button but it says TypeError: undefined is not an object (evaluating 'this.props.navigation.navigate'). I am not sure what I am doing wrong and if I should be passing something from the App.js to LoginComponent.js.
If you tried to print your LoginComponent's props you would end up with nothing!,
But what if you pass the navigation as prop to your component like this! :
// App.js
class LoginScreen extends React.Component {
...
<LoginComponent navigation={this.props.navigation}/>
...
}
You will end up with functional navigation prop.
Happy user details navigation :)

React Native Drawer navigation

Hi there I am trying to implement a drawer navigation in React Native
referring to this example.
I have almost finished the coding but I am getting an error while adding the contentComponent attribute in drawer class (HomeScreenRouter). After removing it I am able to run it successfully and everything is working well but when I add the sidebar menu using contentComponent it throws an error. I need a custom design for my drawer.
Here is my code for the drawer:
import React, { Component } from "react";
import HomeScreen from "./HomeScreen.js";
import MainScreenNavigator from "../DealScreen/mychat.js";
import Profiled from "../profilescreen/Profile.js";
import SideBar from "../SideBar/SideBar.js";
import { DrawerNavigator } from "react-navigation";
const HomeScreenRouter = DrawerNavigator(
{
Home: { screen: HomeScreen },
Chat: { screen: MainScreenNavigator },
Profile: { screen: Profiled },
},
{
contentComponent: props => <SideBar {...props} />
});
export default HomeScreenRouter;
Sidemenu :
import React from "react";
import { AppRegistry, Image, StatusBar } from "react-native";
import { Container, Content, Text, List, ListItem } from "native-base";
export default class SideBar extends React.Component {
render() {
const routes = ["Home", "Chat", "Profile"];
return (
<Container>
<Content>
<Image
source={{
uri: "https://github.com/GeekyAnts/NativeBase-KitchenSink/raw/react-navigation/img/drawer-cover.png"
}}
style={{
height: 120,
alignSelf: "stretch",
justifyContent: "center",
alignItems: "center"
}}>
<Image
square
style={{ height: 80, width: 70 }}
source={{
uri: "https://github.com/GeekyAnts/NativeBase-KitchenSink/raw/react-navigation/img/logo.png"
}}
/>
</Image>
<List
dataArray={routes}
renderRow={data => {
return (
<ListItem
button
onPress={() => this.props.navigation.navigate("Profile")}>
<Text>{data}</Text>
</ListItem>
);
}}
/>
</Content>
</Container>
);
}
}