I've been having trouble resetting the password in my React Native app that uses Supabase and an auth flow. The App function uses the useUser hook to retrieve a Boolean value called AppStacktrue. If it's true, the app is rendered, and if it's false, the auth Stack is rendered:
const AuthStack = () => {
const screenOptions = {
headerShown: false
}
const Stack = createNativeStackNavigator()
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='OpeningScreen' screenOptions={screenOptions}>
<Stack.Screen name='OpeningScreen'component={Home}/>
<Stack.Screen name='Signup' component={Signup}/>
<Stack.Screen name='Login' component={Login}/>
<Stack.Screen name='VerifyOTP' component={AnimatedExample}/>
<Stack.Screen name='Reset-Password' component={ResetPassword}/>
</Stack.Navigator>
</NavigationContainer>
)
}
const AppStack = () => {
const screenOptions = {
headerShown: false
}
const Stack = createNativeStackNavigator()
return (
<NavigationContainer>
<Stack.Navigator initialRouteName='OpeningScreen' screenOptions={screenOptions}>
<Stack.Screen name='OpeningScreen'component={HomeScreen}/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default function App() {
const {AppStacktrue} = useUser()
return (
AppStacktrue?<AppStack/>:<AuthStack/>
);
}
The issue with password reset is that the user must be logged in to change their password. To resolve this, I created the AppStacktrue value in my user.js file. This hook allows the user to log in, log out, sign up, and verify their OTP, but I also want the user to be able to reset their password.
To solve the problem, I created a useEffect that controls the AppStacktrue state. In the verify function (which is used for password reset and sign up), I set a state called Reset to true, but it returns to its initial value automatically. This might be a bug with the onAuthStateChange function. My goal was for the AppStacktrue to be false when the function is active.
I'm not sure what the best solution is to handle the password reset in my case. Any suggestions or ideas would be greatly appreciated!
import { useEffect, useState } from "react";
import { Alert } from "react-native";
import { supabase } from "./supabase";
const useUser = () => {
const [session, setSession] = useState();
const [loading, setLoading] = useState();
const [AppStacktrue, setAppStacktrue] = useState()
const [reset, setReset] = useState()
useEffect(() => {
const getUserProfile = async () => {
const session = await getSession();
if (session) setSession(session.data.session);
}
getUserProfile();
supabase.auth.onAuthStateChange((_event, session) => {
setSession(session)
})
}, []);
useEffect(()=>{ //UseEffect to set the app Stack
console.log(reset)
if(session){
if(reset === true){
setAppStacktrue(false)
}else{
setAppStacktrue(true)
}
}else{
setAppStacktrue(false)
}
}, [session, reset])
async function getSession() {
const session = await supabase.auth.getSession();
return session;
};
const verify = async (email, token, forgotPassword, navigation) => { //here is the problem
setLoading(true)
setReset(true) //returns to inital value
const { error } = await supabase.auth.verifyOtp({
email,
token,
type: forgotPassword ? "recovery" : "signup",
});
setLoading(false)
if (error) {
if (error.status === 400)
return Alert.alert("Bitte gebe volständig Daten an.");
else if (error.status === 401)
return Alert.alert(
"Ungültiger Code",
"Dieser Code ist entweder Abgelaufen, falsch oder du hast versucht einen bereits vorhanden Account zu registrieren. Logge dich in diesem Fall ein.",
[
{
text: "Einloggen",
onPress: () => {
navigation.push("Login");
},
},
{
text: "Schließen",
},
]
);
else return Alert.alert("", error.message);
}
};
async function logout() {
await supabase.auth.signOut()
};
async function login(email, password) {
if (!email || !password)
return Alert.alert("Leere Felder", "Bitte fülle etwas aus.");
setLoading(true)
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
setLoading(false)
if (error) {
if (error.status === 400)
return Alert.alert(
"Falsche Anmeldedaten",
"Deine Email-Adresse oder dein Passwort ist falsch"
);
else Alert.alert(error.message);
}
};
async function signUp(email, password, confirmPassword, Name, navigation) {
if (!Name || !email || !password || !confirmPassword)
return Alert.alert("Bitte gebe vollständige Daten an");
if (!/^[A-Za-z]+ [A-Za-z]+$/.test(Name))
return Alert.alert(
"Ungültiger Name",
"Dein Name muss aus Vor und Nachname bestehen."
);
if (password !== confirmPassword)
return Alert.alert("Deine Passwörter stimmen nicht überein");
if (password.length < 6)
return Alert.alert("Dein Passswort muss mindestens 6 Zeichen lang sein");
setLoading(true);
const { data: profile, error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
full_name: Name,
},
},
});
setLoading(false);
if (error) {
if(error.status === 422){
return Alert.alert('Email adresse kann nicht vearbeitet werden', 'Deine Email-Adresse ist in ungültigem Format')
}
}
else {
if (!profile.user.email) return Alert.alert("Keine Email erhalten");
navigation.push("VerifyOTP", {
email: profile.user.email,
});
}
};
async function resetPassword(email, navigation) {
if (!email) return Alert.alert("Leeres Feld", "Bitte gebe Etwas ein.");
setLoading(true)
const { error } = await supabase.auth.resetPasswordForEmail(email);
setLoading(false)
if (error) {
if (error.status === 400)
return Alert.alert(
"Falsche Anmeldedaten",
"Deine Email-Adresse oder dein Passwort ist falsch"
);
else if (error.status === 429)
return Alert.alert(
"Bitte warte einen Moment",
"Aus sicherheitsgründen kann dies nur alle 60 Sekunden ausgeführt werden."
);
else Alert.alert(error.message);
} else {
navigation.navigate("VerifyOTP", {
email,
forgotPassword: true,
});
}
};
return { AppStacktrue, login, signUp, resetPassword, verify, logout, loading};
};
export default useUser;
I solved the Issue like this:
export const Stack = () => {
const { session } = useUser();
const screenOptions = {
headerShown: false,
};
const Stack = createNativeStackNavigator();
const AppStack = (
<>
<Stack.Screen name="OpeningScreen" component={BottomTabNavigator} />
<Stack.Screen name="ChangePassword" component={ChangePassword} />
</>
);
const AuthStack = (
<>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Signup" component={Signup} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="VerifyOTP" component={VerifyRegisterOtp} />
<Stack.Screen name="Reset-Password" component={ResetPassword} />
</>
);
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="OpeningScreen"
screenOptions={screenOptions}
>
{session?AppStack:AuthStack}
<Stack.Screen name='VerifyPasswordResetOTP' component={VerifyPasswordResetOTP}/>
</Stack.Navigator>
</NavigationContainer>
);
};