I'm building an iOS application in React Native using Expo, and I'm using firebase to handle user creation and login. I have an AuthProvider.js class that basically allows me to set a global state (logged in/logged out) throughout the application.
Here is the AuthProvider.js
import React, { createContext, useContext, useEffect, useState } from 'react';
import { auth } from '../frontend/screens/firebase'; // Import your Firebase auth instance here
const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null); // Initialize user as null
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((authUser) => {
if (authUser) {
setUser(authUser); // Set the user if authenticated
} else {
setUser(null); // Set user to null if not authenticated
}
});
return unsubscribe;
}, []);
return <AuthContext.Provider value={user}>{children}</AuthContext.Provider>;
}
export function useAuth() {
return useContext(AuthContext);
}
Then in App.js
, I've wrapped all my screens in this tag.
This seems to work fine when I login, here is the Login.js
:
import React, { useEffect, useState } from 'react';
import { View, Text, TextInput, TouchableOpacity, ImageBackground, Image } from 'react-native';
import { COLORS, assets } from '../constants';
import { useNavigation } from '@react-navigation/native';
import { auth } from './firebase';
const Login = () => {
const navigation = useNavigation();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged(user => {
if (user) {
navigation.navigate('Home');
}
});
return unsubscribe;
}, []);
const handleGoBack = () => {
navigation.goBack();
};
const handleSignup = () => {
navigation.navigate('Signup'); // Navigate to the login screen
};
const handleLogin = () => {
auth
.signInWithEmailAndPassword(email, password)
.then((userCredential) => {
const user = userCredential.user;
console.log('User logged in successfully:', user.email);
})
.catch(error => alert(error.message));
};
return (
<ImageBackground
source={assets.demoLoginImage}
style={{
flex: 1,
resizeMode: 'cover',
height: '50%',
}}
>
{/* Add a button in the top left corner with an icon */}
<TouchableOpacity onPress={handleGoBack} style={{ position: 'absolute', top: 50, left: 30, zIndex: 1, backgroundColor: COLORS.white, borderRadius: 20 }}>
<Image source={assets.back} style={{ width: 12, height: 12, tintColor: COLORS.secondary, margin: 10 }} />
</TouchableOpacity>
<View
style={{
flex: 1,
height: '65%',
paddingHorizontal: 16,
position: 'absolute',
bottom: 0,
left: 0,
right: 0,
backgroundColor: COLORS.primary,
paddingTop: 20,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
alignItems: 'center',
}}
>
<Text
style={{
fontSize: 24,
fontWeight: 'bold',
marginBottom: 16,
color: 'white',
paddingTop: 5,
}}
>
Log ind
</Text>
<TextInput
style={{
backgroundColor: 'white',
padding: 12,
marginTop: 20,
marginBottom: 20,
borderRadius: 8,
width: '100%',
}}
placeholder="Email"
keyboardType="email-address"
autoCapitalize="none"
onChangeText={(text) => setEmail(text)}
/>
<TextInput
style={{
backgroundColor: 'white',
padding: 12,
marginBottom: 5,
borderRadius: 8,
width: '100%',
}}
placeholder="Password"
secureTextEntry
onChangeText={(text) => setPassword(text)}
/>
<View style={{ flexDirection: 'row', justifyContent: 'flex-end', width: '100%', marginBottom: 20 }}>
<Text
style={{
color: 'white',
fontSize: 14,
marginTop: 5,
marginBottom: 20,
}}
>
Forgot Password?
</Text>
</View>
<TouchableOpacity
onPress={handleLogin}
style={{
backgroundColor: COLORS.secondary,
padding: 16,
borderRadius: 8,
alignItems: 'center',
width: '100%',
}}
>
<Text
style={{
color: 'white',
fontSize: 18,
}}
>
Log ind
</Text>
</TouchableOpacity>
<View style={{ flexDirection: 'row', alignItems: 'center', marginTop: 20 }}>
<TouchableOpacity onPress={() => handleFacebookLogin()}>
<Image source={assets.facebook} style={{ width: 100, height: 100, marginRight: 10 }} />
</TouchableOpacity>
<TouchableOpacity onPress={() => handleGoogleLogin()}>
<Image source={assets.google} style={{ width: 100, height: 100 }} />
</TouchableOpacity>
</View>
<View style={{ flex: 1, justifyContent: 'flex-end', marginBottom: 20 }}>
{/* "Log ind" and the button */}
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ color: 'white', fontSize: 16 }}>Don't have an account? </Text>
<TouchableOpacity onPress={handleSignup}>
<Text style={{ textDecorationLine: 'underline', color: COLORS.white, fontSize: 16 }}>Sign Up</Text>
</TouchableOpacity>
</View>
</View>
</View>
</ImageBackground>
);
};
export default Login;
However when I Logout, I keep getting different kind of errors. For example I've tried this:
import React from 'react';
import { View, Text, Button, SafeAreaView } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { auth } from '../screens/firebase';
import { useAuth } from '../../auth/AuthProvider'; // Import your AuthContext
const Profile = () => {
const navigation = useNavigation();
const { setUser } = useAuth(); // Get the setUser function from AuthContext
const handleLogout = async () => {
try {
// Sign out the user using Firebase
await auth.signOut();
// Set user state to null
setUser(null);
// Navigate to a screen of your choice (e.g., Login)
navigation.navigate('Login');
} catch (error) {
console.error('Logout error:', error);
}
};
return (
<SafeAreaView style={{ flex: 1 }}>
<View>
<Text>Profile Screen</Text>
{/* Add your profile content here */}
<Button title="Logout" onPress={handleLogout} />
</View>
</SafeAreaView>
);
};
export default Profile;
When I click on the logout button it does log out but I get these two errors:
ERROR [TypeError: Cannot read property 'setUser' of null]
Logout error: [TypeError: setUser is not a function (it is undefined)]