Reputation: 173
I'm trying to add firebase auth for sign up, sign in and sign out in my app. I followed the code structure from the react-navigation website. This is the context file...
import React from 'react';
export const AuthContext = React.createContext();
and this is my App file. For the sign-in, I'm using email and password. For the sign-up, I want to add an email, username, and password.
import React, { useContext, useState, useReducer, useEffect, useMemo } from 'react';
import { Home, Profile, Settings, Chat, Phone } from "./src/screens";
import {
NavigationContainer,
DefaultTheme as NavigationDefaultTheme,
DarkTheme as NavigationDarkTheme
} from '@react-navigation/native';
import {
Provider as PaperProvider,
DefaultTheme as PaperDefaultTheme,
DarkTheme as PaperDarkTheme
} from 'react-native-paper';
import { createDrawerNavigator } from '@react-navigation/drawer';
import AsyncStorage from '@react-native-community/async-storage';
import Fire from './src/api/Fire';
import firebase from 'firebase';
import AppTabs from "./src/stacks/AppTabs";
import AuthStack from "./src/stacks/AuthStack";
import { DrawerContent } from "./src/screens/DrawerContent";
import Spinner from "./src/components/Spinner";
import { AuthContext } from './src/components/Context';
const Drawer = createDrawerNavigator();
export default function App() {
const [isDarkTheme, setIsDarkTheme] = useState(false);
{/* Themes */ }
const CustomDefaultTheme = {
...NavigationDefaultTheme,
...PaperDefaultTheme,
colors: {
...NavigationDefaultTheme.colors,
...PaperDefaultTheme.colors,
background: '#ffffff',
text: '#333333'
}
}
const CustomDarkTheme = {
...NavigationDarkTheme,
...PaperDarkTheme,
colors: {
...NavigationDarkTheme.colors,
...PaperDarkTheme.colors,
background: '#333333',
text: '#ffffff'
}
}
const theme = isDarkTheme ? CustomDarkTheme : CustomDefaultTheme;
const [state, dispatch] = useReducer(
(prevState, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...prevState,
userToken: action.token,
isLoading: false,
};
case 'SIGN_IN':
return {
...prevState,
email: action.id,
isSignout: false,
userToken: action.token,
isLoading: false,
};
case 'SIGN_OUT':
return {
...prevState,
email: null,
isSignout: true,
userToken: null,
isLoading: false,
};
case 'REGISTER':
return {
...prevState,
email: action.id,
isLoading: false,
userToken: action.token,
};
}
},
{
isLoading: true,
isSignout: false,
email: null,
userToken: null,
}
);
const authContext = useMemo(() => ({
signIn: async data => {
// In a production app, we need to send some data (usually username, password) to server and get a token
// We will also need to handle errors if sign in failed
// After getting token, we need to persist the token using `AsyncStorage`
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', id: email, token: userToken });
},
signOut: async data => {
dispatch({ type: 'SIGN_OUT' })
},
signUp: async data => {
// In a production app, we need to send user data to the server and get a token
// We will also need to handle errors if sign up failed
// After getting token, we need to persist the token using `AsyncStorage`
// In the example, we'll use a dummy token
dispatch({ type: 'REGISTER', id: email, token: userToken });
},
toggleTheme: () => {
setIsDarkTheme(isDarkTheme => !isDarkTheme);
}
}),
[]
);
useEffect(() => {
setTimeout(async () => {
let userToken;
userToken = null;
try {
userToken = await AsyncStorage.getItem('userToken');
} catch (e) {
console.log(e);
}
dispatch({ type: 'RESTORE_TOKEN', token: userToken });
}, 1000);
}, []);
if (state.isLoading) {
return (
<Spinner />
);
}
return (
<PaperProvider theme={theme}>
<AuthContext.Provider value={authContext}>
<NavigationContainer theme={theme}>
{state.userToken !== null ? (
<Drawer.Navigator drawerContent={props => <DrawerContent {...props} />} >
<Drawer.Screen name="HomeDrawer" component={AppTabs} />
<Drawer.Screen name="ProfileDrawer" component={Profile} />
<Drawer.Screen name="SettingsDrawer" component={Settings} />
<Drawer.Screen name="PhoneDrawer" component={Phone} />
</Drawer.Navigator>
)
:
<AuthStack />
}
</NavigationContainer>
</AuthContext.Provider>
</PaperProvider>
)
}
The firebase API keys have been initialized in this imported file.
import Fire from './src/api/Fire';
I have seen some code that wraps FirebaseAuth around the NavigationContainer
, wouldn't AuthContext.Provider
do the same thing?
I'm calling the SingIn
and SignUp
on their respective screens using useContext
, like this
const { signUp } = useContext(AuthContext); // signup screen
const { signIn } = useContext(AuthContext); // signin screen
then using them in the onPress function like onPress={()=>{signUp(email, username, paswword)}}
Upvotes: 3
Views: 544
Reputation: 173
I got an idea of what to do from this link is found: use-auth
For the useReducer hook
const [state, dispatch] = useReducer(
(prevState, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...prevState,
user: action.payload.user,
userToken: action.token,
isLoading: false,
};
case 'SIGN_IN':
return {
...prevState,
user: action.payload.user,
isSignout: false,
userToken: action.token,
isLoading: false,
};
case 'SIGN_OUT':
return {
...prevState,
user: null,
isSignout: true,
userToken: null,
isLoading: false,
};
case 'REGISTER':
return {
...prevState,
isLoading: false,
};
default:
throw new Error(`No case for type ${action.type} found.`)
}
},
{
isLoading: true,
isSignout: false,
userToken: null,
user: null,
}
);
authContext
function getRef() {
return firebase.database().ref();
}
const authContext = useMemo(() => ({
signIn: async (email, password, user) => {
await firebase.auth().signInWithEmailAndPassword(email.trim(), password)
.then(() => {
state.isLoading = false
})
.catch(function (error) {
// Handle Errors here.
const errorCode = error.code
const errorMessage = error.message
alert(errorMessage)
state.isLoading = false
})
dispatch({
type: 'SIGN_IN',
payload: {
user: firebase.auth().currentUser,
},
});
},
signOut: async () => {
firebase.auth().signOut()
.then(function () {
// Sign-out successful.
state.isLoading = false
})
.catch(function (error) {
// An error happened.
state.isLoading = false
})
dispatch({ type: 'SIGN_OUT' })
},
signUp: async (email, password, avatar, displayName, phoneNumber, about) => {
try {
await firebase.auth().createUserWithEmailAndPassword(email.trim(), password)
.then((userInfo) => {
userInfo.user.updateProfile({
displayName: displayName,
photoURL: avatar,
phoneNumber: phoneNumber,
})
console.log(userInfo);
})
.then(() => {
firebase.auth().onAuthStateChanged(user => {
getRef().child('users')
.push({
avatar: avatar,
email: email,
name: displayName,
phoneNumber: phoneNumber,
aboutMe: about,
uid: user.uid
})
})
})
}
catch (error) {
alert(error);
}
dispatch({
type: 'REGISTER'
})
},
toggleTheme: () => {
setIsDarkTheme(isDarkTheme => !isDarkTheme);
}
}),
[]
);
useEffect hook
useEffect(() => {
setTimeout(async () => {
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// User is signed in.
if (onAuthStateChange.current) {
onAuthStateChange.current = false
return
}
dispatch({
type: 'RESTORE_TOKEN',
payload: {
user,
},
})
} else {
// User is signed out.
dispatch({
type: 'SIGN_OUT',
payload: {
user: null,
},
})
}
})
}, 1000);
}, []);
And the return function
return (
<PaperProvider theme={theme}>
<AuthContext.Provider value={authContext}>
<NavigationContainer
theme={theme}
>
{state.user !== null ? (
<Drawer.Navigator drawerContent={props => <DrawerContent {...props} />} >
<Drawer.Screen name="HomeDrawer" component={AppTabs} />
<Drawer.Screen name="ProfileDrawer" component={Profile} />
<Drawer.Screen name="SettingsDrawer" component={Settings} />
<Drawer.Screen name="PhoneDrawer" component={Phone} />
</Drawer.Navigator>
)
:
<AuthStack />
}
</NavigationContainer>
</AuthContext.Provider>
</PaperProvider>
)
If you notice something used incorrectly, please advise. Otherwise, this is what's working for me, for now. I'm still trying to keep the state of the theme when it is changed, even after the app is restarted.
Upvotes: 2