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.