React Native firebase Authentication handling - javascript

I am trying to create a simple Auth flow in my Expo application.
I've reacreated my project here so you can see what I am doing
Right now what happens in my app is I can register a user using Firebase authtentication, but can't navigate to the authenticated flows. Nothing happens after I click register, but I can see the user in firebase.
I have been reading the React Navigaion docs and tried implementing something similar to what they do for the Auth flow there, but have not had any success.
I think the issue has to do with how I am getting the token in App.js and my use of useEffect
App.js
import React, { useEffect } from 'react';
import * as eva from '#eva-design/eva';
import { Provider as AuthProvider } from './src/context/AuthContext';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { createStackNavigator } from '#react-navigation/stack';
import { ApplicationProvider } from '#ui-kitten/components';
import { NavigationContainer } from '#react-navigation/native';
import LandingScreen from './src/screens/LandingScreen';
import RegisterScreen from './src/screens/RegisterScreen';
import LoginScreen from './src/screens/LoginScreen';
import HomeScreen from './src/screens/HomeScreen';
import DetailScreen from './src/screens/DetailScreen';
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();
/*eslint-disable */
export default function App() {
let token;
useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const localSignin = async () => {
try {
token = await AsyncStorage.getItem('userToken');
} catch (e) {
// Restoring token failed
}
};
console.log(token);
localSignin();
}, []);
return (
<ApplicationProvider {...eva} theme={eva.light}>
<AuthProvider>
<NavigationContainer>
{token ? (
<>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Details" component={DetailScreen} />
</Drawer.Navigator>
</>
) : (
<>
<Stack.Navigator>
<Stack.Screen name="Landing" component={LandingScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
</Stack.Navigator>
</>
)}
</NavigationContainer>
</AuthProvider>
</ApplicationProvider>
);
}
Should I be using my AuthContext here somewhere? I am very new to react development so I am sorry if this is somewhat unclear.

Your "token" is manipulated asynchronously, so it should be a state variable so your component may re-render when it got changed. To achieve that, you should replace your token declaration with
const [token, setToken] = React.useState(undefined);
After that, when you want to change your token, instead of reassigning to the token variable, call setToken with the desired value. Like this:
setToken(await AsyncStorage.getItem('userToken'));
The final code should look like this
import React, { useEffect } from 'react';
import * as eva from '#eva-design/eva';
import { Provider as AuthProvider } from './src/context/AuthContext';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { createStackNavigator } from '#react-navigation/stack';
import { ApplicationProvider } from '#ui-kitten/components';
import { NavigationContainer } from '#react-navigation/native';
import LandingScreen from './src/screens/LandingScreen';
import RegisterScreen from './src/screens/RegisterScreen';
import LoginScreen from './src/screens/LoginScreen';
import HomeScreen from './src/screens/HomeScreen';
import DetailScreen from './src/screens/DetailScreen';
const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();
/*eslint-disable */
export default function App() {
const [token, setToken] = React.useState(undefined);
useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const localSignin = async () => {
try {
setToken(await AsyncStorage.getItem('userToken'));
} catch (e) {
// Restoring token failed
}
};
console.log(token);
localSignin();
}, []);
return (
<ApplicationProvider {...eva} theme={eva.light}>
<AuthProvider>
<NavigationContainer>
{token ? (
<>
<Drawer.Navigator initialRouteName="Home">
<Drawer.Screen name="Home" component={HomeScreen} />
<Drawer.Screen name="Details" component={DetailScreen} />
</Drawer.Navigator>
</>
) : (
<>
<Stack.Navigator>
<Stack.Screen name="Landing" component={LandingScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="Login" component={LoginScreen} />
</Stack.Navigator>
</>
)}
</NavigationContainer>
</AuthProvider>
</ApplicationProvider>
);
}

Related

Issue with UseNavigation in react native expo in App.js (Followed up from the previous issue)

Another issue that arrives from the previous issue: Issues with header button in React Native Expo
After changing from ../firebase to ./firebase. It says it couldnt find variable of navigation. So I import { useNavigation } from '#react-navigation/core' and also added const navigation = useNavigation(). But I am having the issue of
Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
It seems to be able to sign out as I have the console logging of Signed Out Successfully but it cannot navigate back to the login
Am I doing something wrong here? My idea of adding this button on the right side at the border is for the user to be able to sign out faster when the user is logged in the app
Updated App.js
import { StatusBar } from 'expo-status-bar';
import React from 'react'
import { signOut } from 'firebase/auth';
import { auth } from './firebase';
import { useNavigation } from '#react-navigation/core'
import { StyleSheet, Text, View, Button } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from './screens/LoginScreen';
import HomeScreen from './screens/HomeScreen';
import RegisterScreen from './screens/RegisterScreen';
import ForgetPasswordScreen from './screens/ForgetPasswordScreen';
import SubScreen1 from './screens/SubScreen1';
import SubScreen2 from './screens/SubScreen2';
const Stack = createNativeStackNavigator();
const navigation = useNavigation()
const handleSignOut = async () =>{
try{
await signOut(auth)
console.log("Signed out successfully")
navigation.replace("Login")
}catch (error) {
console.log({error});
}
}
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen options= {{ headerShown : false }} name="Login" component={LoginScreen} />
<Stack.Screen name="Home" component={HomeScreen} options={{headerRight: () => (
<Button
onPress={handleSignOut}
title="Sign Out"
color="#0782F9" /> ),}} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="Forget Password" component={ForgetPasswordScreen} />
<Stack.Screen name="SubScreen1" component={SubScreen1} options={{ title: 'Search Result' }}/>
<Stack.Screen name="SubScreen2" component={SubScreen2} options={{ title: 'Random Search Result' }}/>
</Stack.Navigator>
</NavigationContainer>
);
}
You are breaking the rules of hooks in React. Its basically initializing the hook outside a component.
Put the hooks inside the component like here:
export default function App() {
// Use the hook here
const navigation = useNavigation();
// Add the function here also, its generally preferred to
// put functions inside the componenent so it can get access to hooks. Or you can also
// pass the hook as an argument if you prefer.
const handleSignOut = async () => {
try {
await signOut(auth);
console.log("Signed out successfully");
navigation.replace("Login");
} catch (error) {
console.log({ error });
}
}
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen options= {{ headerShown : false }} name="Login" component={LoginScreen} />
<Stack.Screen name="Home" component={HomeScreen} options={{headerRight: () => (
<Button
onPress={handleSignOut}
title="Sign Out"
color="#0782F9" /> ),}} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="Forget Password" component={ForgetPasswordScreen} />
<Stack.Screen name="SubScreen1" component={SubScreen1} options={{ title: 'Search Result' }}/>
<Stack.Screen name="SubScreen2" component={SubScreen2} options={{ title: 'Random Search Result' }}/>
</Stack.Navigator>
</NavigationContainer>
);
}
Hope this helps.

How to remove default styles from react-navigation?

I was making an app with react native and react-navigation. So what I did was I made a Login screen. Then I used react-navigation to create a native stack navigator and linked it to my Login screen. I successfully rendered the Login Screen but there seems to be some sort of default styling on the stack navigator(?). How do I remove or overwrite those styles so that the original styling of my screens come back? Images and Code below.
This is the stack navigator
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from '../screens/LoginScreen';
import SignUpScreen from '../screens/SignUpScreen';
const Stack = createNativeStackNavigator();
const AuthStack = () => {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login" screenOptions={{ header: () => null }}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Signup" component={SignUpScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default AuthStack;
When I only render the LoginScreen it looks like this
When I use AuthStack it looks like this
Should have read the docs nicely it was there in the NativeStackNavigator options.
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import LoginScreen from '../screens/LoginScreen';
import SignUpScreen from '../screens/SignUpScreen';
const Stack = createNativeStackNavigator();
const AuthStack = () => {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Login"
screenOptions={{
headerShown: false,
header: () => null,
contentStyle: { backgroundColor: 'white' },
}}
>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Signup" component={SignUpScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default AuthStack;
To do your own style do this
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer, DefaultTheme } from "#reactnavigation/native";
const MyTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: Color.red,
},
};
Then do this
<NavigationContainer theme={MyTheme}>
<Stack.Navigator initialRouteName="startscreen" headerMode="none" >
</Stack.Navigator>
</NavigationContainer>
More information here (https://reactnavigation.org/docs/themes/)

Pass props from App class to const function React Typescript

Im trying to create an React Native App with Expo.
I have a logical error passing props from App class to my Component "Navigator" type const function.
Im not sure how i must to pass the props or how i must to access to them without passing from App:
App.tsx
import React, {useState} from 'react';
import { NavigationContainer, StackActions } from '#react-navigation/native';
import Navigator from './Components/Navigator';
export default class App extends React.Component {
render(){
return (
<NavigationContainer>
<Navigator props={this.props}/>
</NavigationContainer>
);
}
}
Navigator.tsx
import React from 'react'
import { createStackNavigator } from '#react-navigation/stack'
import Login from '../Views/Login'
import Home from '../Views/Home'
const Stack = createStackNavigator();
function Navigator({props}){
return (
<Stack.Navigator>
<Stack.Screen name="Login" options={{title:'Login.'}}>
{(props) => <Login {...props} />}
</Stack.Screen>
<Stack.Screen name="Home" component={Home} options={{title:'Home.'}}/>
</Stack.Navigator>
);
}
export default Navigator;
I have error undefined is not an object (props)
This happens because you are make destructuring
Then when you call props it's like doing props.props
function Navigator(props){// remove { } from this line

How to change initial screen in Route.Js with Async Storage in React Native

I am trying to change the entrance point my application in Router.js to not show the user Onboarding screens second time. Basically I set an async storage value. When the onboarding is finished the value return true. As the async storage functions are async, the return statement in Router.js is not waiting the it to return a value. So I can’t change the initial point of my navigation system.
index.js
AppRegistry.registerComponent(appName, () => Router);
Router.js
function Router() {
let first ="App"
let second ="GetStarted"
return (
<NavigationContainer>
<Stack.Navigator initialRouteName={checkFirstUsage().then(data=>data?second:first)} screenOptions={{headerShown: false} }>
<Stack.Screen name="GetStarted" component={GetStarted} />
<Stack.Screen name="EnterPoem" component={EnterPoem} />
<Stack.Screen name="Interest" component={Interested} />
<Stack.Screen name="Begin" component={Begin} />
<Stack.Screen name="App" component={App} />
</Stack.Navigator>
</NavigationContainer>
);
}
checkFirstUsage
const checkFirstUsage = async () => {
try {
const value = await AsyncStorage.getItem('#isAsyncStorageSet')
if(value === null) {
return true
}
else{
return false;
}
} catch(e) {
console.log(e)
}
}
any help is appreciated
I think the best approach when you want to manage separated stack navigators is something like
index.js
import React, {useState, useEffect} from 'react';
import OnBoardingRoutes from './onBoarding.routes';
import AppRoutes from './app.routes';
import checkFirstUsage from "./checkFirstUsage/path";
const Routes: React.FC = () => {
const [loading, setLoading] = useState(true)
const [firstUsage,setFirstUsage] =useState(null);
useEffect(() => {
async function check() {
const fU = await checkFirstUsage()
setFirstUsage(fU)
setLoading(false)
}
check()
},[])
if (loading) return null // or any better component
return firstUsage ? <OnBoardingRoutes /> : <AppRoutes />;
};
export default Routes;
And then you have your two stacks navigator, with their respective routes
onBoarding.routes
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import OnBoardingPage1 from '../pages/OnBoardingPage1';
import OnBoardingPage2 from '../pages/OnBoardingPage2';
const App = createStackNavigator();
const OnBoardingRoutes: React.FC = () => (
<App.Navigator
initialRouteName="GetStarted"
screenOptions={{
headerShown: false,
}}
>
<App.Screen name="OnBoardingPage1" component={OnBoardingPage1} />
<App.Screen name="OnBoardingPage2" component={OnBoardingPage2} />
</App.Navigator>
);
export default OnBoardingRoutes;
and your App routes
app.routes
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import AppPage1 from '../pages/AppPage1';
import AppPage2 from '../pages/AppPage2';
const App = createStackNavigator();
const AppRoutes: React.FC = () => (
<App.Navigator
initialRouteName="App"
screenOptions={{
headerShown: false,
}}
>
<App.Screen name="AppPage1" component={AppPage1} />
<App.Screen name="AppPage2" component={AppPage2} />
</App.Navigator>
);
export default AppRoutes;
and your Router component will looks like
import React from "react"
import Routes from "./index";
function Router() {
return (
<NavigationContainer>
<Routes/>
</NavigationContainer>
);
}
export default Router;

How do I use standalone Header in react-navigation with react-native?

I have a standalone Header that I made:
import React, { useContext } from "react";
import { Appbar } from "react-native-paper";
import { UserContext } from "../contexts/UserContext";
import { LanguageContext } from "../contexts/LanguageContext";
import localeSelect from "../services/localeSelect";
import { title } from "../data/locales";
function Header() {
const { user } = useContext(UserContext);
const { language } = useContext(LanguageContext);
return (
<Appbar.Header>
<Appbar.Action icon="menu" />
{!user && (
<>
<Appbar.Content
title={localeSelect(language, title)}
color="#ffffff"
/>
<Appbar.Action
icon="login"
color="#ffffff"}
/>
<Appbar.Action icon="account-plus" color="#ffffff" />
</>
)}
{user && (
<>
<Appbar.Content
title={localeSelect(language, title)}
color="#ffffff"
/>
</>
)}
</Appbar.Header>
);
}
export default Header;
However, I have been struggling to find a way to connect it to my Stack.Navigator in the main component:
import "react-native-gesture-handler";
import React, { useContext } from "react";
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import { View } from "react-native";
import Header from "./components/Header";
import Home from "./components/Home";
import Login from "./components/Login";
import GameNotes from "./components/GameNotes";
import { UserContext } from "./contexts/UserContext";
const Stack = createStackNavigator();
export default function ComponentContainer() {
const { user } = useContext(UserContext);
return (
<View>
<NavigationContainer>
<Header />
<Stack.Navigator initialRouteName="Home">
{user ? (
<Stack.Screen name="Home" component={GameNotes} />
) : (
<Stack.Screen name="Home" component={Home} />
)}
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
</View>
);
}
Obviously this doesn't work, because the header isn't nested in the Stack.Navigator. You also cannot nest it in the Stack.Navigator, because it only accepts Stack.Screen as children.
I've also tried using RootNavigation as a way to do this, but have failed with that as well. So what exactly am I supposed to be doing to use this Header to navigate my app?
So you want to change something in the Stack.Navigator when some action happens in the Header? Most likely what you need is to pass a callback to the Header component from the ComponentContainer, which Header will call when a user clicks something. Inside this callback you can modify your state in order to change the Stack.Navigator or take some other action. The callback will be defined inside ComponentContainer.
I'm not exactly sure how this all works in react-native, but in React this is what it might look like:
Header:
// Imports
function Header(onPressCallback) {
const { user } = useContext(UserContext);
const { language } = useContext(LanguageContext);
return (
<Appbar.Header>
<Appbar.Action icon="menu" onPress={() => onPressCallback('menu')} />
// Other Header items
</Appbar.Header>
);
}
export default Header;
ComponentContainer:
// Imports
const Stack = createStackNavigator();
const onPressHandler = (headerItemName) => {
// Do something in response to a user clicking on the "menu" icon for example
// Maybe something like change the route on the Stack (don't know this API)
}
export default function ComponentContainer() {
const { user } = useContext(UserContext);
return (
<View>
<NavigationContainer>
<Header onPressCallback={onPressHandler} />
<Stack.Navigator initialRouteName="Home">
{user ? (
<Stack.Screen name="Home" component={GameNotes} />
) : (
<Stack.Screen name="Home" component={Home} />
)}
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
</View>
);
}
This is how you usually handle interactions between two siblings components, the parent component passes a callback to one, so that the first child can notify when something happened, and then the parent takes action by changing the state for the second child to respond to.

Categories

Resources