Currently I have a simple React Native app which works with signing in and signing out. The only problem is that in order for the sign in to complete or for the signing out to complete (ie, take me to the other screen) I must refresh the application.
I was wondering if there is something wrong in my code which stops user rendering which screen to display.
StackNavigator.js:
import React from "react"; import { createStackNavigator } from "@react-navigation/stack"; import { HomeScreen, LoginScreen, RegistrationScreen } from "../screens"; import useAuth from "../hooks/useAuth"; const Stack = createStackNavigator(); const StackNavigator = () => { const { user } = useAuth(); return ( <Stack.Navigator> {user ? ( // if user is logged in we show the home screen <Stack.Screen name="Home" component={HomeScreen} /> ) : ( <> <Stack.Screen name="Login" component={LoginScreen} /> <Stack.Screen name="Registration" component={RegistrationScreen} /> </> )} </Stack.Navigator> ); }; export default StackNavigator;
useAuth.js:
import React, { createContext, useContext, useEffect, useState } from "react"; import { auth } from "../model/config"; import { signInWithEmailAndPassword, createUserWithEmailAndPassword, } from "firebase/auth"; const AuthContext = createContext({}); export const AuthProvider = ({ children }) => { const [user, setUser] = useState(null); //user is initally null const [loadingInital, setLoadingInital] = useState(true); //loadingInital is initally true const [loading, setLoading] = useState(false); //loading is initally false useEffect(() => { //This hook allows us to remember the user's login status const unsub = auth.onAuthStateChanged((user) => { if (user) { setUser(user); } else { setUser(null); } setLoadingInital(false); //after user is fetched, loadingInital is set to false }); return unsub(); //unsubscribe from the auth listener }, []); const onLoginPress = (email, password) => { setLoading(true); //loading is set to true //Takes in two arguments, email and password used in LoginScreen class signInWithEmailAndPassword(auth, email, password) .then((userCredential) => { // Signed in const user = userCredential.user; console.warn("signed in"); console.warn(user); //Navigate after sign in // ... }) .catch((error) => { //If any error we will catch const errorCode = error.code; if (errorCode === "auth/user-not-found") { console.warn("User not found"); } if (errorCode === "auth/wrong-password") { console.warn("Wrong password"); } else { console.warn(error); } }); setLoading(false); //loading is set to false }; const onRegisterPress = (email, password, confirmPassword) => { setLoading(true); //loading is set to true if (password !== confirmPassword) { alert("Passwords don't match."); return; } createUserWithEmailAndPassword(auth, email, password) .then((userCredential) => { const user = userCredential.user; alert("Welcome"); }) .catch((error) => { alert(error); }); setLoading(false); //loading is set to false }; const signOut = () => { setLoading(true); //loading is set to true auth.signOut(); console.warn(user); setLoading(false); //loading is set to false }; return ( <AuthContext.Provider value={{ user, onLoginPress, onRegisterPress, signOut, loading }} > {!loadingInital && children} </AuthContext.Provider> ); }; //if loadingInital is true, we will render nothing ^ export default function useAuth() { return useContext(AuthContext); }
LoginScreen:
import React, { useState } from "react"; import { Image, Text, TextInput, TouchableOpacity, View } from "react-native"; import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view"; import styles from "./styles"; //styles import FINAL_STYLES from ".././../FINAL_STYLES"; //styles main import { auth } from "../../model/config"; import { getAuth, signInWithEmailAndPassword } from "firebase/auth"; import useAuth from "../../hooks/useAuth"; export default function LoginScreen({ navigation }) { const { onLoginPress } = useAuth(); const [email, setEmail] = useState(""); //setting as temp state const [password, setPassword] = useState(""); const onFooterLinkPress = () => { navigation.navigate("Registration"); }; //Below we return JSX return ( <View style={styles.container}> <KeyboardAwareScrollView style={{ flex: 1, width: "100%" }} keyboardShouldPersistTaps="always" > <Image style={styles.logo} source={require("../../../assets/icon.png")} /> <TextInput style={styles.input} placeholder="E-mail" placeholderTextColor="#aaaaaa" onChangeText={(text) => setEmail(text)} value={email} underlineColorAndroid="transparent" autoCapitalize="none" /> <TextInput style={styles.input} placeholderTextColor="#aaaaaa" secureTextEntry placeholder="Password" onChangeText={(text) => setPassword(text)} value={password} underlineColorAndroid="transparent" autoCapitalize="none" /> <TouchableOpacity style={[styles.button, { backgroundColor: "#9D3BEA" }]} //TODO: change to global styles onPress={() => onLoginPress(email, password)} > <Text style={styles.buttonTitle}>Log in</Text> </TouchableOpacity> <View style={styles.footerView}> <Text style={styles.footerText}> Don't have an account?{" "} <Text onPress={onFooterLinkPress} style={styles.footerLink}> Sign up </Text> </Text> </View> </KeyboardAwareScrollView> </View> ); }
What would be the best way to get it to update immediately after I sign in? Thank you 🙂
Advertisement
Answer
You have to reload the application to see changes because in onLoginPress
you are mutating user
state without calling setUser()
, while in that useEffect
that runs on load you did the correct thing. Change it as below. Notice that setUser(userCredential.user)
I added:
const onLoginPress = (email, password) => { setLoading(true); signInWithEmailAndPassword(auth, email, password) .then((userCredential) => { setUser(userCredential.user); }) .catch((error) => { const errorCode = error.code; if (errorCode === "auth/user-not-found") { console.warn("User not found"); } if (errorCode === "auth/wrong-password") { console.warn("Wrong password"); } else { console.warn(error); } }) .finally(() => { // to make sure it runs after the promise has resolved setLoading(false); }); };
You made the same mistake in onRegisterPress
as well, don’t forgot to fix it. Lastly in signOut
, call setUser(null)
after auth.signOut()
to make sure you reset user
.