Navigate after login - react-native

I'm creating an app using React-Native. I added Firebase Auth to my application, but I can't navigate to the Homescreen after log in.
This is my code:
constructor (in LoginScreen):
constructor(props){
super(props)
this.state = {
email: '',
password: '',
status: '',
}
this.handlePress = this.handlePress.bind(this)
}
app.js:
import React from 'react';
import { AppRegistry } from 'react-native';
import { StackNavigator } from 'react-navigation';
import LoginScreen from './app/screens/LoginScreen';
import RegisterScreen from './app/screens/RegisterScreen';
import HomeScreen from './app/screens/HomeScreen';
const Stylelist = StackNavigator({
Login: { screen: LoginScreen },
Register: { screen: RegisterScreen},
Home: {screen: HomeScreen},
},{headerMode: "none"});
export default Stylelist;
handlePress function(function in the Loginscreen):
handlePress(){
firebaseRef.auth().signInWithEmailAndPassword(this.state.email, this.state.password).then(function(firebaseUser){
//Success, move to homepage.
console.log("logged in!")
navigate("Home")
}).catch(function(error){
//Failed to log in, print error.
});
}
This "logged in" does gets printed in the console but won't move to the Homescreen.
this is the render method in the loginScreen.js:
import React, { Component } from 'react';
import { View, StyleSheet, Text, TouchableOpacity, KeyboardAvoidingView, TextInput, StatusBar, Image } from 'react-native';
import { firebaseRef } from '../services/Firebase';
export default class LoginScreen extends Component{
constructor(props){
super(props)
this.state = {
email: '',
password: '',
status: '',
}
this.handlePress = this.handlePress.bind(this)
}
//Trying to login the account with email and password provided by the user.
handlePress(){
firebaseRef.auth().signInWithEmailAndPassword(this.state.email, this.state.password).then(function(firebaseUser){
//Success, move to homepage.
console.log("logged in!")
this.props.navigation.navigate("Home");
}).catch(function(error){
//Failed to log in, print error.
});
}
render(){
const { navigate } = this.props.navigation;
return(
<KeyboardAvoidingView behavior="padding" style={styles.container}>
<StatusBar
barStyle="light-content"
/>
<View style={styles.logoContainer}>
<Image
style={styles.logo}
source={require('../assets/Logo.png')}/>
</View>
<View style={styles.formContainer}>
<TextInput
style={styles.txtInput}
keyboardType="email-address"
placeholder="email"
onChangeText={(text)=> this.setState({email: text})}
placeholderTextColor="#FFFFFF"
onSumbitEditing={()=>this.passwordInput.focus()}
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
/>
<TextInput
style={styles.txtInput}
placeholder="password"
placeholderTextColor="#FFFFFF"
onChangeText={(text)=> this.setState({password: text})}
returnKeyType="go"
autoCapitalize="none"
autoCorrect={false}
secureTextEntry
ref={(input)=>this.passwordInput = input}
/>
<TouchableOpacity style={styles.logBtn} onPress={this.handlePress}>
<Text style={styles.logTxt}>
Login
</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.regBtn} onPress={()=>navigate("Register")}>
<Text style={styles.regTxt}>
create account
</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
}
I don't get any error printed and the app does not crash, what am I doing wrong?

I think you are missing the context inside the promise and navigate no longer exists there. So let's pass a parameter navigate callback to make it work:
Firstly, add navigate as a parameter to handlePress
handlePress(navigate){
firebaseRef.auth().signInWithEmailAndPassword(this.state.email, this.state.password).then(function(firebaseUser){
//Success, move to homepage.
console.log("logged in!")
navigate("Home");
}).catch(function(error){
//Failed to log in, print error.
});
}
Secondly, modify the TouchableOpacity to call handlePress properly:
...
<TouchableOpacity style={styles.logBtn} onPress={() => this.handlePress(navigate)}>
<Text style={styles.logTxt}>
Login
</Text>
</TouchableOpacity>
...

I know this was resolved but, no resolution was posted here. If anyone is having issues make sure you can access the navigation prop (try console logging it) another issue may be if you're conditionally rending your navigation stack based if auth exists, that will mean that one stack does not have access to the to other

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.

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

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

Navigate to a new screen by calling a function onPress

I would like to use my onLogin() function to navigate to the Dashboard view of the Android MVP I'm building. I've been thrown into a React Native project despite not knowing it and I've only just begun my career, so the answer is probably painfully obvious, but after lots of research I can't quite work it out! Hopefully one of you can guide me to the solution.
I've copied the Login view below.
import React, { Component } from 'react';
import {
TouchableHighlight,
TextInput,
Text,
View,
Image
} from 'react-native';
import styles from "./../style/CustomStylesheet";
export default class Login extends Component {
constructor(props) {
super(props);
this.state = {
Email: '',
password: '',
};
}
onLogin() {
const { Email, password } = this.state;
//insert function to navigate to dashboard here?
}
render() {
return (
<View style={styles.container}>
<Image
style={styles.logo}
source={require('./../../android/app/src/main/res/drawable/login.png')}
/>
<TextInput
value={this.state.Email}
onChangeText={(Email) => this.setState({ Email })}
placeholder={'Email'}
placeholderTextColor={'#333333'}
style={styles.input}
inlineImageLeft='login_id'
/>
<TextInput
value={this.state.password}
onChangeText={(password) => this.setState({ password })}
placeholder={'Password'}
placeholderTextColor={'#333333'}
secureTextEntry={true}
style={styles.input}
inlineImageLeft='login_password'
/>
<View style={styles.help}>
<Text>Need help?</Text>
</View>
<TouchableHighlight
style={[styles.input, styles.button]}
onPress={this.onLogin.bind(this)}>
<Text style={styles.buttonText}>LOGIN</Text>
</TouchableHighlight>
<View style={styles.modal}>
<Text style={styles.modalText}>New user?</Text>
<Text style={styles.modalText}>Register on our web app or our iPad app</Text>
</View>
</View>
);
}
}
I've added the dashboard to my Stack Navigation file too. Any guidance you could give would be wonderful. Many thanks!
import Login from "./Login";
import Dashboard from "./Dashboard";
const AppNavigator = createStackNavigator(
{
Home: { screen: Login },
Dashboard: { screen: Dashboard }
},
{
headerMode: 'none'
}
);
export default createAppContainer(AppNavigator);
add this
onLogin=()=> {
const { Email, password } = this.state;
const { navigation } = this.props;
navigation.navigate('Dashboard');
}

How to jump another page after login/sign up

I build navigation with react-navigation v3 and auth with firebase. No problem with that. Navigation flow is works and I can sign up. The problem I am facing is, when I push the Sign up button it doesn't jump to Signup Screen.
So the structure that Build: in App.js I am doing navigation part. First sending Welcome Screen which include Login.
This is Welcome Screen:
import React, { Component } from 'react'
import {StyleSheet, View } from "react-native";
import {Container, Text, Form, Content, Header, Button, Input, Label, Item} from 'native-base';
import SignUp from '../screens/register/SignUp'
import * as firebase from 'firebase';
const firebaseConfig = {
apiKey: "example example",
authDomain: "example example",
databaseURL: "example example",
projectId: "example example",
storageBucket: "example example",
};
firebase.initializeApp(firebaseConfig);
export default class WelcomeScreen extends Component {
constructor(props){
super(props)
this.state = ({
email: '',
password: ''
})
}
loginUser = (email, password, navigate) => {
try {
firebase.auth().signInWithEmailAndPassword(email,password).then(function(user){
console.log(user);
navigate('Learn')
})
}
catch (error) {
console.log(error.toString())
}
};
render() {
return (
<Container style={styles.container}>
<Form>
<Item floatingLabel>
<Label>E-mail</Label>
<Input
autocorrect={false}
autoCapitalize={'none'}
onChangeText={(email) => this.setState({email})}
/>
</Item>
<Item floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry={true}
autocorrect={false}
autoCapitalize={'none'}
onChangeText={(password)=>this.setState({password})}
/>
</Item>
</Form>
<Button style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.loginUser(this.state.email,this.state.password)}
rounded
success
>
<Text>Kelimeda'ya Uç</Text>
</Button>
<Button style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.props.navigation.navigate('SignUp')}
rounded
primary
>
<Text>Beni Kaydet!</Text>
</Button>
</Container>
);
}
}
Sign Up Screen:
import React, { Component } from 'react'
import {StyleSheet, View } from "react-native";
import {Container, Text, Form, Content, Header, Button, Input, Label, Item} from 'native-base';
import * as firebase from 'firebase';
export default class WelcomeScreen extends Component {
constructor(props){
super(props)
this.state = ({
email: '',
password: ''
})
}
signUpUser = (email, password) => {
try {
if(this.state.password.length < 6){
alert('Lutfen 6 dan daha uzun bir karakter giriniz.')
return
}
firebase.auth().createUserWithEmailAndPassword(email,password)
}
catch (error) {
console.log(error.toString())
}
};
render() {
return (
<Container style={styles.container}>
<Form>
<Item floatingLabel>
<Label>E-mail</Label>
<Input
autocorrect={false}
autoCapitalize={'none'}
onChangeText={(email) => this.setState({email})}
/>
</Item>
<Item floatingLabel>
<Label>Password</Label>
<Input
secureTextEntry={true}
autocorrect={false}
autoCapitalize={'none'}
onChangeText={(password)=>this.setState({password})}
/>
</Item>
</Form>
<Button style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.signUpUser(this.state.email,this.state.password)}
rounded
primary
>
<Text>Beni Kaydet!</Text>
</Button>
</Container>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 5,
justifyContent: 'center',
backgroundColor: '#fff',
},
});
and this is the App Screen. Do I need to check here user Logged in or Not? or Welcome Screen?
//imports...
import React, { Component } from 'react';
import {View, StatusBar} from 'react-native';
import {
createSwitchNavigator,
createAppContainer,
createDrawerNavigator,
createBottomTabNavigator,
createStackNavigator,} from 'react-navigation';
import Icon from 'react-native-vector-icons/Ionicons';
import WelcomeScreen from './src/screens/Welcome';
import Learn from './src/screens/Tab/Learn';
import Settings from './src/screens/Tab/Settings';
import Play from './src/screens/Tab/Play';
//content: const & functions and styles...
const DashboardTabNavigator = createBottomTabNavigator({
Play,
Learn,
Settings
},
{
navigationOptions: ({navigation}) => {
const {routeName} = navigation.state.routes
[navigation.state.index];
return {
headerTitle: routeName,
headerTintColor:'#fff',
headerStyle:{
backgroundColor: '#2c3e50',
}
};
}
});
const DashStack = createStackNavigator({
DashboardTabNavigator: DashboardTabNavigator
}, {
defaultNavigationOptions:({navigation}) =>{
return {
headerLeft: <Icon
style={{paddingLeft: 15, color:'#fff'}}
onPress={()=>navigation.openDrawer()}
name={'md-menu'}
size={30}
/>
}
},
});
const appDrawNavigator = createDrawerNavigator({
Dashboard:{ screen: DashStack }
});
const appSwitchNavigation = createSwitchNavigator({
Welcome:{ screen: WelcomeScreen },
Dashboard:{ screen: appDrawNavigator }
});
const AppContainer = createAppContainer(appSwitchNavigation);
class App extends Component {
render() {
return(
<View style={{flex: 1}}>
<StatusBar
backgroundColor="blue"
barStyle="light-content"
/>
<AppContainer/>
</View>
) ;
}
}
export default App;
Your login function looks like this:
loginUser = (email, password, navigate) => {
try {
firebase.auth().signInWithEmailAndPassword(email,password).then(function(user){
console.log(user);
navigate('Learn')
})
}
catch (error) {
console.log(error.toString())
}
};
This function expecting three parameters. You should pass this.props.navigation.navigate to the login function to use navigate('Learn')
<Button
style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.loginUser(this.state.email,this.state.password,this.props.navigation.navigate)}
rounded
success
>
Solution for problem 1: You have not passed navigate to your loginUser function, so it is not working. Please send the navigate param to the loginUser like this.
<Button
style={{backgroundColor:'#6c5ce7', marginTop: 10}}
onPress={()=>this.loginUser(this.state.email,this.state.password, this.props.navigation.navigate)}
rounded
success>
Solution for problem 2: For the firebase duplication issue, its because you are initialising the firebase instance twice in the application. What you should do instead is, just initialize the firebase at the application root component like App.js or Splash screen, so that it can be available throughout the app lifecycle and whenever you need to use it just import it and use.
Solution for problem 3: Its a common usecase as to know the logged in status of the user upfront to navigate the user appropriately into the application.
For this, what you can do is, just save a flag in AsyncStorage for eg. isLoggedIn as YES upon successful login, and post this, whenever the app is opened just analyse with the flag's presence/value wether the user is logged in or not. A good place to do this is either your app's Splashscreen component or the root component of the application.
Edited Answer (additional): (for problem 1)
You have your navigation routes nested wrongly to directly jump to Learn from
welcome screen, to navigate from one screen to another, the two screen should be in same navigation scope (if navigator is given as a route in another navigator then the route navigator is considered as a screen and the user will be navigated to its initial route when navigated to it)
Your code should target navigating to Dashboard route, which will internally render the nested navigators i.e. the first/initial route but with current nesting this will land you to Play so what can be done is, make Learn as first/initial route of your tab navigator.
The code in the loginUser should be navigate('Dashboard') and your tab navigator should have Learn as its initial route.

error: "undefined is not an object(evaluating 'this,props.navigation.navigate')" In React Native

Getting errors when navigating one page to other page in React Native.
error:
undefined is not an object(evaluating 'this,props.navigation.navigate')
code:
import { StackNavigator,NavigationActions } from "react-navigation";
const Navigation = StackNavigator({
Home : {
screen : Home
},
})
export default class App extends React.Component {
submit = () => {
this.props.navigation.navigate('Home');
}
render() {
return (
<View style={styles.container}>
<Text>Enter Log</Text>
<TextInput style={styles.input}
multiline={true}
underlineColorAndroid="transparent"
placeholder="Enter Log"
placeholderTextColor="#9a73ef"
autoCapitalize="none"
onChangeText={this.handlePassword} />
<TouchableOpacity style={styles.submitButton} onPress={() => submit } >
<Text style={styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
);
}
}
}
You have to bind your method:
import { StackNavigator,NavigationActions } from "react-navigation";
const Navigation = StackNavigator({
Home : {
screen : Home
},
})
export default class App extends React.Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
render() {
return (
<View style={styles.container}>
<Text>Enter Log</Text>
<TextInput style={styles.input}
multiline={true}
underlineColorAndroid="transparent"
placeholder="Enter Log"
placeholderTextColor="#9a73ef"
autoCapitalize="none"
onChangeText={this.handlePassword} />
<TouchableOpacity style={styles.submitButton} onPress={this.submit} >
<Text style={styles.submitButtonText}> Submit </Text>
</TouchableOpacity>
</View>
);
}
submit() {
this.props.navigation.navigate('Home');
}
}
Explanation: Binding context when calling ES6 method. How to access object from within method called as callback?
Try changing your onPress value to this:
onPress={this.submit}
Also, I don't see you importing your Home component to where you assign it as a screen.