skip to Main Content

I am currently trying to make a workable app using expo react native and I encounter some issues while doing it.
My issue is before, I followed a youtube guide to code my login and register on the same page in the application and I find it being too simple. So I want to make it look more like the modern login/register page. Thus I try to create a link from the login page to the register page by the button Don't have an account?. This page worked as it created a account for me when I clicked the button register but at the same time, it auto log in the user (newly created) straight away. In the function for handling this sign up, navigation.replace("Login") was in the funtion but it does not seem to work.
What I intended to do is to after successfully registering, it will direct me to the login page where the user has to key in the new created details at the login page to login.
Can anyone help me with this? Thank you.

Here is my code:

App.js

import { StatusBar } from 'expo-status-bar';
import React from 'react'
import { StyleSheet, Text, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import LoginScreen from './screens/LoginScreen';
import HomeScreen from './screens/HomeScreen';
import RegisterScreen from './screens/RegisterScreen';


const Stack = createNativeStackNavigator();


export default function App() {
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen options= {{ headerShown : false }} name="Login" component={LoginScreen} />
        <Stack.Screen name="Home" component={HomeScreen} />
        <Stack.Screen name="Register" component={RegisterScreen} />
        
      </Stack.Navigator>
    </NavigationContainer>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

LoginScreen.js

import { KeyBoardAvoidingView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
import React, { useEffect, useState } from 'react'
import KeyboardAvoidingView from 'react-native/Libraries/Components/Keyboard/KeyboardAvoidingView'
import { auth } from '../firebase'
import { createUserWithEmailAndPassword, signInWithEmailAndPassword } from "firebase/auth"
import { useNavigation } from '@react-navigation/core'


const LoginScreen = () => {
    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')
    const [showError, setShowError] = useState(false);
    const navigation = useNavigation()

    useEffect(() => {
        const unsubscribe = auth.onAuthStateChanged(user => {
            if(user){
                navigation.replace("Home")
            }
        })

        return unsubscribe
    })


    const handleLogin = async () => {
        try {
        if (email && password) {
           const { user } = await signInWithEmailAndPassword(auth, email, password)
           console.log('Logged in as :' , user.email);
          }
        } catch (error) {
           console.log({error});
           setShowError(true);
        }
    }    
    return (
    <KeyboardAvoidingView  //To prevent keyboard from blocking the writing area
        style={styles.container}
        behavior = "padding"
    >  
        <View style = {styles.inputContainer}> 
            <Text>Email:</Text>
            <TextInput
                placeholder = "Email"
                value={email}
                onChangeText ={text => setEmail(text)}
                style = {styles.input} 
            />
            <Text></Text>
            <Text>Password:</Text>         
            <TextInput
                placeholder = "Password"
                value={password}
                onChangeText ={text => setPassword(text)}
                style = {styles.input} 
                secureTextEntry //Hide password
            />         
        </View> 
        {showError && <View style={styles.error}>
          <Text>Incorrect username or password</Text>
        </View>}        
        <View style = {styles.buttonContainer}>
            <TouchableOpacity
                onPress = {handleLogin}
                style = {styles.button}
            >
                <Text style={styles.buttonText}>Login</Text>
            </TouchableOpacity>
        </View> 
        <Text></Text>
        <View style = {{flexDirection:"row"}}>
            <TouchableOpacity
                onPress = { () => navigation.navigate("Register")}
                style = {styles.text}
            >
                <Text style={styles.text}>Don't have an account?                 </Text>
            </TouchableOpacity>
            <TouchableOpacity
                //onPress = {}
                style = {[styles.text]}
            >
                <Text style={styles.text}>Forget Password?</Text>
            </TouchableOpacity>
            
           
        </View> 
                          
    </KeyboardAvoidingView> 
  )
}

export default LoginScreen

const styles = StyleSheet.create({ //To make the text area for username and password to be center. If not it will be at the top left hand corner
    container:{
        flex:1,
        justifyContent: 'center',
        alignItems:'center',
    },
    inputContainer:{
        width: '80%'
    },
    input:{ //Not working?
        backgroundColor: 'white',
        paddingHorizontal: 15,
        paddingVertical: 10,
        borderRadius: 10, //make edge more circle
        marginTop: 5, //Spacing between the input boxes

    },
    buttonContainer:{
        width: '60%',
        justifyContent: 'center',
        alignItems : 'center',
        marginTop: 40,
    },
    button:{
        backgroundColor: '#32CD32',
        width: '100%',
        padding: 15, //Making the button enlarge in horizontal and vertical
        borderRadius: 10,
        alignItems: 'center', 

    },
    buttonOutline:{
        backgroundColor: 'white',
        marginTop: 5,
        borderColor: '#008000', 
        borderWidth: 2, //Without the width, the colour won't show
    },
    buttonText:{ //For the login button
        color: 'white',
        fontWeight:'700',
        fontSize: 16,
    },
    buttonOutlineText:{ //For the register button
        color: '#0782F9',
        fontWeight:'700',
        fontSize: 16,
    },
   error: {
      backgroundColor: 'red',
      borderRadius:10,
      marginTop: 5,
      padding: 16,
      textAlign: 'center'
   },
   text:{
       color: '#0000FF',
       fontStyle: 'italic',
       marginTop: 5,
   }
})

RegisterScreen.js

import { StyleSheet, Text, View, KeyboardAvoidingView, TextInput, TouchableOpacity } from 'react-native'
import React from 'react'
import { useNavigation } from '@react-navigation/native';
import { createUserWithEmailAndPassword } from "firebase/auth"
import { useState } from 'react'
import { auth } from '../firebase'

const RegisterScreen = () => {
    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')
    const [showError, setShowError] = useState(false);
    const navigation = useNavigation()
    const handleSignUp = async () => {
        try {
        if (email && password) {
           setShowError(false);
           const { user } = await createUserWithEmailAndPassword(auth, email, password)
           console.log('Registered as :' , user.email);
           navigation.replace("Login")
          }
        } catch (error) {
           console.log({error});
           setShowError(true);
        }
    }
    return (
    <KeyboardAvoidingView  //To prevent keyboard from blocking the writing area
        style={styles.container}
        behavior = "padding"
    >  
        <View style = {styles.inputContainer}> 
            <Text>Email:</Text>
            <TextInput
                placeholder = "Email"
                value={email}
                onChangeText ={text => setEmail(text)}
                style = {styles.input} 
            />
            <Text></Text>
            <Text>Password:</Text>         
            <TextInput
                placeholder = "Password (Min: 6 chars)"
                value={password}
                onChangeText ={text => setPassword(text)}
                style = {styles.input} 
                secureTextEntry //Hide password
            />         
        </View> 
        {showError && <View style={styles.error}>
          <Text>Username or password not valid</Text>
        </View>}        
        <View style = {styles.buttonContainer}>
            <TouchableOpacity
                onPress = {handleSignUp}
                style = {styles.button}
            >
                <Text style={styles.buttonText}>Register</Text>
            </TouchableOpacity>
        </View>                            
    </KeyboardAvoidingView> 
  )
}

export default RegisterScreen

const styles = StyleSheet.create({
    container:{
        flex:1,
        justifyContent: 'center',
        alignItems:'center',
    },
    inputContainer:{
        width: '80%'
    },
    input:{ //Not working?
        backgroundColor: 'white',
        paddingHorizontal: 15,
        paddingVertical: 10,
        borderRadius: 10, //make edge more circle
        marginTop: 5, //Spacing between the input boxes

    },
    buttonContainer:{
        width: '60%',
        justifyContent: 'center',
        alignItems : 'center',
        marginTop: 40,
    },
    button:{
        backgroundColor: '#32CD32',
        width: '100%',
        padding: 15, //Making the button enlarge in horizontal and vertical
        borderRadius: 10,
        alignItems: 'center', 

    },
})

HomeScreen.js

import { TouchableOpacity, StyleSheet, Text, View } from 'react-native'
import React from 'react'
import { auth } from '../firebase'
import { useNavigation } from '@react-navigation/core'
import { signOut } from 'firebase/auth'

const HomeScreen = () => {

  const navigation = useNavigation()
  const handleSignOut = async () =>{
    try{
      await signOut(auth)
      console.log("Signed out successfully")
      navigation.replace("Login")
    }catch (error) {
      console.log({error});
   }
  }
  return (
    <View style = {styles.container}>
      <Text>Email: {auth.currentUser?.email}</Text>
      <TouchableOpacity
      onPress={handleSignOut}
        style = {styles.button}
      >
        <Text style = {styles.buttonText}>Sign Out</Text>
      </TouchableOpacity>
    </View>
  )
}

export default HomeScreen

const styles = StyleSheet.create({
  container:{
    flex:1,
    justifyContent:'center',
    alignItems:'center'
  },
  button:{
    backgroundColor: '#0782F9',
    width: '60%',
    padding: 15, //Making the button enlarge in horizontal and vertical
    borderRadius: 10,
    alignItems: 'center', 
    marginTop: 40,
  },
  
  buttonText:{ //For the login button
    color: 'white',
    fontWeight:'700',
    fontSize: 16,
  },
})

The part that deals with the listener

import {useNavigationState} from '@react-navigation/native'


const LoginScreen = () => {
    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')
    const [showError, setShowError] = useState(false);
    const navigation = useNavigation()
    const routes = useNavigationState((state) => state.routes);
    const currentRoute = routes[routes.length - 1].name;

    useEffect(() => {
        const unsubscribe = auth.onAuthStateChanged(user => {
            if(user && currentRoute == 'Register'){
                console.log(currentRoute)
                navigation.replace("Login")
            }
            if(user && currentRoute == 'Login')
          {
              console.log(currentRoute)
              navigation.replace("Home")
          }
        })

        return unsubscribe
    })

Alternative method:

import { useIsFocused } from '@react-navigation/native';



const LoginScreen = () => {
    const [email, setEmail] = useState('')
    const [password, setPassword] = useState('')
    const [showError, setShowError] = useState(false);
    const navigation = useNavigation()
    let isFocused = useIsFocused;


    useEffect(() => {
        const unsubscribe = auth.onAuthStateChanged(user => {
            if(user && isFocused){
                navigation.replace("Home")
            }
        })

        return unsubscribe
    })

According to the logs,
enter image description here

How do i read this? The above logs came out when I clicked register

2

Answers


  1. This page worked as it created a account for me when I clicked the
    button "register" but at the same time, it auto log in the user (newly
    created) straight away.

    By using the JS SDK createUserWithEmailAndPassword() method the newly created user is automatically signed in when the method call is successful. You cannot prevent that.

    So if you want to avoid the redirection you need to remove the navigation.replace("Login") line. If you don’t want the user to be signed in you could immediately call the signOut() method (technically speaking the user is signed in for a very short time and then signed out).

    Login or Signup to reply.
  2. In your LoginScreen you have put

     const unsubscribe = auth.onAuthStateChanged(user => {
                if(user){
                    navigation.replace("Home")
                }
            })
    

    which is a Kind of listener and listening to authentication state change. So as you are doing a registration the firebase auth state is getting changed and listener getting called and then you are landing to HomeScreen. This explanation will come into Picture only if you have navigation stack likewise -> App Open -> LoginScreen -> RegisterScreen.

    If you make your navigation likewise -> App Open -> Register -> LoginScreen, You will not auto logged into the app. Because you haven’t navigated to the LoginScreen and that listener not become active.

    Even though if you are facing the same problem, then kill the application and restart it.

    If you do not want to alter your navigation flow though then you will have to put some condition here in below given code :

    Fetch current Route likewise : –

          import {useNavigationState} from '@react-navigation/native';
    
          //Inside your LoginScreen
          const routes = useNavigationState((state) => state.routes);
          const currentRoute = routes[routes.length - 1].name;
    
    auth.onAuthStateChanged(user => {
               //Some handling to move on which screen
               //You can put a check on currently active route here
               //based on that you can navigate accordingly
              if(user && currentRoute == 'Login')
              {
                  //navigate to HomeScreen
              }
            }) 
    

    Alternative to currentRoute :

    import { useIsFocused } from '@react-navigation/native';
    
    //inside component 
    let isFocused = useIsFocused;
    
     const unsubscribe = auth.onAuthStateChanged(user => {
                if(user && isFocused){
                     //It means Login is focused 
                     //means we should redirect to HomeScreen from here 
                 }
        })
    

    Alternative 2 :

    const [isLogin, setIsLogin] = useState(false); 
    
    
    const unsubscribe = auth.onAuthStateChanged(user => {
                if(isLogin){
                    navigation.replace("Home")
                }
            })
    
    const handleLogin = async () => {
        try {
        if (email && password) {
           const { user } = await signInWithEmailAndPassword(auth, email, password);
           setIsLogin(true);
           console.log('Logged in as :' , user.email);
          }
        } catch (error) {
           console.log({error});
           setShowError(true);
        }
    } 
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search