How to successfully logout and navigate back to the main App screen in React-Native? - javascript

I need help figuring out the right way to logout and return to the Welcome screen.
Currently, the RootStack queries the redux store for a token, and uses that to determine which group of screens will be available to the user.
If a token exists we go into a TabNavigator called OrgTabs. And that seems to be taking removing the parent navigator. And when I try to logout, the screen I want to navigate to doesn't exist within the navigation object.
Please help! I'd really appreciate
Rootstack.js
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
//screens
import Welcome from '../screens/Welcome';
import SignIn from '../screens/SignIn';
import UserSignIn from '../screens/UserSignIn.js';
import StaffSignIn from '../screens/StaffSignIn';
import StaffSignUp from '../screens/StaffSignUp';
import OrgSignIn from '../screens/OrgSignIn';
import OrgSignUp from '../screens/OrgSignUp';
import OrgHomeScreen from '../screens/org/OrgHomeScreen';
import OrgTabs from '../components/molecules/OrgTabs';
import TestScreen from '../screens/org/TestScreen';
// selecetors and hooks
import { useSelector } from 'react-redux';
import { selectToken, selectRefreshToken } from '../features/user/userSlice';
const Stack = createStackNavigator();
const RootStack = () => {
const token = useSelector(selectToken)
const refreshToken = useSelector(selectRefreshToken)
return (
<NavigationContainer>
<Stack.Navigator
screenOptions={{
headerShown: true,
headerTitle:'',
headerTransparent: true
}}
>
{ token ?
(<Stack.Group>
<Stack.Screen name="OrgTabs" component={OrgTabs} />
<Stack.Screen name="Welcome" component={Welcome} />
<Stack.Screen name="TestScreen" component={TestScreen} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="UserSignIn" component={UserSignIn} />
<Stack.Screen name="StaffSignIn" component={StaffSignIn} />
<Stack.Screen name="StaffSignUp" component={StaffSignUp} />
<Stack.Screen name="OrgSignIn" component={OrgSignIn} />
<Stack.Screen name="OrgSignUp" component={OrgSignUp} />
</Stack.Group>
)
:
// auth screens
(<Stack.Group>
<Stack.Screen name="Welcome" component={Welcome} />
<Stack.Screen name="SignIn" component={SignIn} />
<Stack.Screen name="UserSignIn" component={UserSignIn} />
<Stack.Screen name="StaffSignIn" component={StaffSignIn} />
<Stack.Screen name="StaffSignUp" component={StaffSignUp} />
<Stack.Screen name="OrgSignIn" component={OrgSignIn} />
<Stack.Screen name="OrgSignUp" component={OrgSignUp} />
<Stack.Screen name="OrgTabs" component={OrgTabs} />
</Stack.Group>
)
}
</Stack.Navigator>
</NavigationContainer>
);
}
export default RootStack;
StaffSignIn.js
import React from "react";
import { Text, TextInput, ActivityIndicator } from "react-native";
import { StatusBar } from 'expo-status-bar';
import { Formik, Form } from "formik";
import { useDispatch, useSelector } from "react-redux";
import {
LightContainer, PadlessContainer, FlexHoriztal,
Header1, Header2, Header3, TextLight,
AppLogo, StlyedButton,
Pad_h_medium, Pad_h_small, Pad_w_small} from "../styles/styles";
import { TextLink, MsgBox } from "../components/atoms/Atoms";
import { StyldTextInput } from "../components/molecules/Molecules";
import { StyledFormArea } from "../components/organisms/Organisms";
import KeyboardAvoidingWrapper from "../components/organisms/KeyboardAvoidingWrapper";
import * as SecureStore from "expo-secure-store";
import axios from "axios";
import { setToken } from "../features/user/userSlice";
import {getToken} from "../features/user/User";
const logo_img = require("../assets/logo_red.png");
const chalk = require('chalk');
const StaffSignIn = ({navigation}) => {
[message, setMessage] = React.useState("");
[messageStatus, setMessageStatus] = React.useState("failed");
[hidePassword, setHidePassword] = React.useState(true);
const dispatch = useDispatch();
const handleSubmit = (values, setSubmitting) => {
const url = "https://dandle.dustinc.dev/signin/staff";
axios.post(url, values)
.then( (response) => {
const result = response.data;
const {success, user, token, refreshToken} = response.data;
if(success === true) {
setMessageStatus("success");
setMessage("sign in successful");
storeToken(token, refreshToken);
dispatch(setToken(token));
navigation.navigate("OrgTabs");
}
else if (success === false) {
setMessageStatus("failed");
setMessage("sign in failed");
}
setSubmitting(false);
})
.catch(err => {
setSubmitting(false);
// setMessage("Oops! Network error. Try again soon");
if (err.response.status === 401) {
setMessage("Invalid username or password");
}
else {
setMessage("Oops! Network error. Try again soon");
}
})
.finally(() => {
setSubmitting(false);
});
}
// function that will store a token and refresh token in react-native-keychain
async function storeToken (token, refreshToken) {
SecureStore.setItemAsync("token", token)
.then(() => {
SecureStore.setItemAsync("refreshToken", refreshToken)
.then(() => {
dispatch(setToken(token));
dispatch(setToken(refreshToken));
console.log("\x1b[32m successfully stored token and refresh-token\n");
console.log("\x1b[0m token: ", token,'\n');
console.log("refreshToken: ", refreshToken, '\n');
})
.catch(err => {
console.log(err);
});
})
.catch(err => {
console.log(err);
});
}
return (
<KeyboardAvoidingWrapper>
<LightContainer>
<StatusBar style="dark" />
<PadlessContainer>
<Pad_h_medium /><Pad_h_medium />
<Formik
initialValues={{
username: "",
password: "",
}}
onSubmit={(values, {setSubmitting}) => {
handleSubmit(values, setSubmitting);
}}
>
{
({handleChange, handleBlur, handleSubmit, isSubmitting, values}) => (
<StyledFormArea justify='center'>
<StyldTextInput
label="Username"
placeholder="johndoe"
onChangeText={handleChange("username")}
value={values.username}
/>
<StyldTextInput
label="Password"
placeholder="* * * * * * *"
secureTextEntry={hidePassword}
value={values.password}
onChangeText={handleChange('password')}
isPassword={true}
hidePassword={hidePassword}
setHidePassword={setHidePassword}
/>
<Pad_h_medium /><Pad_h_medium /><Pad_h_medium />
<Pad_h_medium /><Pad_h_medium /><Pad_h_medium />
<MsgBox type={messageStatus}>{message}</MsgBox>
{!isSubmitting && <StlyedButton onPress={handleSubmit} width='100%'>
<TextLight>Sign in</TextLight>
</StlyedButton>}<Pad_h_small />
{isSubmitting && <StlyedButton width='100%' disabled={true}>
<ActivityIndicator size="small" color="#fff" />
</StlyedButton>}<Pad_h_small />
<FlexHoriztal justify='center'>
<Header2>Don't have an account?</Header2>
<TextLink onPress={()=> navigation.navigate('StaffSignUp')}>
<Header2>Register</Header2>
</TextLink>
</FlexHoriztal>
</StyledFormArea>
)
}
</Formik>
</PadlessContainer>
</LightContainer>
</KeyboardAvoidingWrapper>
);
}
export default StaffSignIn;
OrgTabs.js
import * as React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import {Octicons} from '#expo/vector-icons';
//screens
import OrgSettings from '../../screens/org/OrgSettings';
import OrgHomeScreen from '../../screens/org/OrgHomeScreen';
import OrgAnalytics from '../../screens/org/OrgAnalytics';
import OrgChat from '../../screens/org/OrgChat';
const Tab = createBottomTabNavigator();
export default function OrgTabs() {
return (
<NavigationContainer
independent={true}
>
<Tab.Navigator
screenOptions={{
tabBarShowLabel: false,
tabBarStyle: {
position: 'absolute',
backgroundColor: '#f38484',
borderTopRightRadius: 2,
borderTopLeftRadius: 2,
},
headerTransparent : true,
headerShown: false,
tabBarActiveBackgroundColor: '#d64547',
}}
>
<Tab.Screen
name="Settings"
component={OrgSettings}
options={{
tabBarIcon: () => {
return(
<Octicons name='gear' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Home"
component={OrgHomeScreen}
options={{
tabBarIcon: () => {
return(
<Octicons name='home' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Chat"
component={OrgChat}
options={{
tabBarIcon: ({size,focused,color}) => {
return(
<Octicons name='comment-discussion' size={25} color='white'/>
)
}
}}
/>
<Tab.Screen
name="Analytics"
component={OrgAnalytics}
options={{
tabBarIcon: ({size,focused,color}) => {
return(
<Octicons name='graph' size={25} color='white'/>
)
}
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}

So the solution I came up with, was to display the message "Logged out" and then set the token in redux to Null.
And finally, reload the app using the Updates package, which is part of Expo.
So when you logout, the app is reloaded and the check for a token is repeated.

Try this,I Hope it will work.
const logout=()=>{
console.log("logout");
AsyncStorage.getAllKeys()
.then(keys => AsyncStorage.multiRemove(keys)).then(() => navigation.reset({ index: 0, routes: [{ name: "Login" }], }));
}

Related

How to solve undefined is not a function

I'm making a ChatApp on ReactNative, on this part I'm trying to implement a SignOut function, but i don't understand what is wrong here.
enter image description here
The error is "undefined is not a function (near '...navigation.setOptions...')
The error is on the function:
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity style={{ marginRight: 10 }} onPress={onSignOut}>
//<AntDesign name="logout" size={24} color={"#808080"} style={{marginRight:10}}/>
</TouchableOpacity>
)
});
}, [navigation]);
import React, {
useState,
useEffect,
useLayoutEffect,
useCallback,
} from "react";
import { TouchableOpacity, Text } from "react-native";
import { GiftedChat } from "react-native-gifted-chat";
import {
collection,
addDoc,
query,
onSnapshot
} from 'firebase/firestore';
import { signOut } from 'firebase/auth';
import { database } from "../config/firebase";
import { useNavigation } from "#react-navigation/native";
import {AntDesign} from "#expo/vector-icons";
import { Colors } from "react-native/Libraries/NewAppScreen";
import { auth } from "../config/firebase"
export default function Chat() {
const [messages, setMessages] = useState([]);
const navigation = useNavigation();
const onSignOut = () => {
signOut(auth).catch(error => console.log(error));
};
useLayoutEffect(() => {
navigation.setOptions({
headerRight: () => (
<TouchableOpacity style={{ marginRight: 10 }} onPress={onSignOut}>
//<AntDesign name="logout" size={24} color={"#808080"} style={{marginRight:10}}/>
</TouchableOpacity>
)
});
}, [navigation]);
useLayoutEffect(() => {
const collectionRef = collection(database, 'chats');
const q = query(collectionRef, orderBy('createAt', 'desc'));
const unsubscribe = onSnapshot(q, snapshot => {
console.log('snapshot');
setMessages(
snapshot.docs.map(doc => ({
_id: doc.id,
createdAt: doc.data().createdAt,
text: doc.data().text,
user: doc.data().user,
}))
)
});
return () => unsubscribe();
}, []);
const onSend = useCallback((messages = []) => {
setMessages(previousMessages => GiftedChat.append(previousMessages, messages));
const { _id, createdAt, text, user } = messages[0];
addDoc(collection(database, 'chats'), {
_id,
createdAt,
text,
user,
});
}, []);
return (
<GiftedChat
messages={messages}
onSend={messages => onSend(messages)}
user = {{
_id: auth?.currentUser?.email,
avatar: 'https://i.pravatar.cc/300'
}}
/>
);
}
The Navigation is on this part
This is the main part of the code, here i make the navigation into de login and signup part and the chat and home screens.
'''App.js Javascript
import React, {useState, createContext, useContext, useEffect} from "react";
import { NavigationContainer } from "#react-navigation/native";
import { createStackNavigator } from "#react-navigation/stack";
import { View, ActivityIndicator } from "react-native";
import { onAuthStateChanged } from "firebase/auth";
import Chat from "./src/screens/Chat";
import Login from "./src/screens/Login";
import Signup from "./src/screens/Signup";
import Home from "./src/screens/Home"
import { auth } from "./src/config/firebase";
const Stack = createStackNavigator();
const AuthenticatedUserContext = createContext({});
const AuthenticatedUserProvider = ({ children }) => {
const [user, setUser] = useState(null);
return (
<AuthenticatedUserContext.Provider value = {{user, setUser}}>
{children}
</AuthenticatedUserContext.Provider>
)
}
function AuthStack () {
return (
<Stack.Navigator defaultScreenOptions={Login} screenOptions={{headerShown: false}}>
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup" component={Signup} />
</Stack.Navigator>
)
}
function ChatStack () {
<Stack.Navigator defaultScreenOptions={Home}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="Chat" component={Chat} />
</Stack.Navigator>
}
function RootNavigator () {
const {user, setUser} = useContext(AuthenticatedUserContext);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth,
async authenticatedUser => {
authenticatedUser ? setUser(authenticatedUser) : setUser(null);
setLoading(false);
}
);
return () => unsubscribe();
}, [user]);
if(loading) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator size="large" />
</View>
)
}
return (
<NavigationContainer>
{ user ? <ChatStack /> : <AuthStack />}
</NavigationContainer>
)
}
export default function App(){
return (
<AuthenticatedUserProvider>
<RootNavigator />
</AuthenticatedUserProvider>
)
}
'''

Navigate to another StackNavigtor screen on button press React Native

I have made a Main Navigator in which I have made 2 sub navigators:
This is my MainNavigator:
import React, {useEffect, useState} from 'react';
import {createStackNavigator} from '#react-navigation/stack';
import storage from '../services/storage';
import {useDispatch, useSelector} from 'react-redux';
import {setUser} from '../store/reducers/auth';
import AuthNavigation from './AuthNavigation';
import MainNavigation from './MainNavigation';
const {Navigator, Screen} = createStackNavigator();
const StackNavigation = () => {
const [isLogged, setIsLogged] = useState(false);
const dispatch = useDispatch();
const loadUser = async () => {
try {
const data = await storage.get('USER');
if (data !== null && data !== '') {
console.log('Hello user', data);
dispatch(setUser(data));
setIsLogged(true);
}
} catch (error) {
console.log('APP JS ERROR', error.message);
}
};
useEffect(() => {
loadUser();
}, []);
return (
<Navigator screenOptions={{headerShown: false}}>
{isLogged ? (
<Screen name="MainNavigation" component={MainNavigation} />
) : (
<Screen name="AuthNavigation" component={AuthNavigation} />
)}
</Navigator>
);
};
export default StackNavigation;
These are my sub navigators:
AuthNavigation:
import React from 'react';
import {createStackNavigator} from '#react-navigation/stack';
import SignUpScreen from '../screens/AccountScreens/SignUpScreen';
import LoginScreen from '../screens/AccountScreens/LoginScreen';
import ForgotPassword from '../screens/AccountScreens/ForgotPassword';
import OTPVerification from '../screens/AccountScreens/OTPVerification';
import SetNewPassword from '../screens/AccountScreens/SetNewPassword';
const {Navigator, Screen} = createStackNavigator();
const AuthNavigation = () => {
const verticalAnimation = {
gestureDirection: 'vertical',
cardStyleInterpolator: ({current, layouts}) => {
return {
cardStyle: {
transform: [
{
translateY: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.height, 0],
}),
},
],
},
};
},
};
return (
<Navigator
initialRouteName={'Login'}
screenOptions={{headerShown: false, animationEnabled: true}}>
<Screen
name="SignUp"
component={SignUpScreen}
options={verticalAnimation}
/>
<Screen name="Login" component={LoginScreen} />
<Screen name="ForgotPassword" component={ForgotPassword} />
<Screen name="OTPVerification" component={OTPVerification} />
<Screen name="SetNewPassword" component={SetNewPassword} />
</Navigator>
);
};
export default AuthNavigation;
MainNavigation:
import React, {useState} from 'react';
import {createStackNavigator} from '#react-navigation/stack';
import MyTabNavigation from './MyTabNavigation';
import GymDetailScreen from '../screens/InnerScreens/GymDetailScreen';
import AccountOverview from '../screens/DrawerScreens/AccountOverview';
import SubscriptionDetails from '../screens/DrawerScreens/SubscriptionDetails';
import BillingDetails from '../screens/DrawerScreens/BillingDetails';
import ChangePassword from '../screens/DrawerScreens/ChangePassword';
import BuySubscribtion from '../screens/InnerScreens/BuySubscribtion';
import ScanScreen from '../screens/InnerScreens/ScanScreen';
import PaymentSummary from '../screens/InnerScreens/PaymentSummary';
import PaymentMethod from '../screens/InnerScreens/PaymentMethod';
const {Navigator, Screen} = createStackNavigator();
const MainNavigation = () => {
const horizontalAnimation = {
cardStyleInterpolator: ({current, layouts}) => {
return {
cardStyle: {
transform: [
{
translateX: current.progress.interpolate({
inputRange: [0, 1],
outputRange: [layouts.screen.width, 0],
}),
},
],
},
};
},
};
return (
<Navigator
initialRouteName={'MyTabNavigation'}
screenOptions={{headerShown: false, animationEnabled: true}}>
<Screen name="GymDetailScreen" component={GymDetailScreen} />
<Screen
name="MyTabNavigation"
component={MyTabNavigation}
options={horizontalAnimation}
/>
<Screen name="AccountOverview" component={AccountOverview} />
<Screen name="SubscriptionDetails" component={SubscriptionDetails} />
<Screen name="BillingDetails" component={BillingDetails} />
<Screen name="ChangePassword" component={ChangePassword} />
<Screen name="BuySubscription" component={BuySubscribtion} />
<Screen name="ScanScreen" component={ScanScreen} />
<Screen name="PaymentSummary" component={PaymentSummary} />
<Screen name="PaymentMethod" component={PaymentMethod} />
</Navigator>
);
};
export default MainNavigation;
I want to go to MainNavigator on Login which is in AuthNavigator, Here is how I am doing this:
navigation.navigate('MainNavigation');
But its giving me this error:
The action 'NAVIGATE' with payload {"name":"MainNavigation"} was not handled by any navigator.
Do you have a screen named 'MainNavigation'?
If you're trying to navigate to a screen in a nested navigator, see https://reactnavigation.org/docs/nesting-navigators#navigating-to-a-screen-in-a-nested-navigator.
This is a development-only warning and won't be shown in production.
Please help me with this coz I am trying this for the first time. Thanks in advance
You are using Authentication flows stated in React Navigation documents.
Please be noted that this approach using conditionally rendering screens which means screen is not rendered if condition is not fulfilled. So, application will cause error for navigation to non-existing screen.
What you need to do in order to jump to MainNavigation screen, is to set your
isLogged variable in MainNavigator to true.
navigation.navigate('MainNavigation');
Here you can not use navigate method because both are different Navigators
So you have to render your navigator on a Conditional base.
For ex:
you can save authTocken after Successful Login and in your main Route file
you can check write condition whether authTocken is null or not.
On that basis you can render your navigator Like,
{
authTocken != '' (
<Stack.Navigator screenOptions={{ headerShown: false }} initialRouteName={initialRoute}>
<Stack.Screen name={routes.Dasboard} component={Dashboard} />
<Stack.Screen name={routes.Setting} component={Setting} />
</Stack.Navigator>
)
:
<Stack.Navigator screenOptions={{ headerShown: false }}
initialRouteName={initialRoute}>
<Stack.Screen name={routes.Login} component={Login} />
<Stack.Screen name={routes.Registration} component=Registration} />
</Stack.Navigator>
}

ERROR The action 'NAVIGATE' with payload {...} was not handled by any navigator in React Native

I have implemented AWS custom UI authenctication in my react native app and React navigation to navigate through the different screens.
While implementing the logical conditions to see if the "User is already logged in or not" I have assigned the screen "Home" for logged in user and screen 'Login' for not logged in user it's working fine and navigating as expected but the console is showing this error when clicking on login button.
ERROR The action 'NAVIGATE' with payload {"name":"Home"} was not handled by any navigator.
Here is the Navigation code:
import React, {useEffect, useState} from 'react'
import {ActivityIndicator, Alert, View} from 'react-native';
import HomeScreen from '../screens/HomeScreen';
import LoginScreen from '../screens/LoginScreen';
import RegisterScreen from '../screens/RegisterScreen';
import ConfirmEmailScreen from '../screens/ConfirmEmailScreen';
import ForgotPassword from '../screens/ForgotPassword';
import NewPasswordScreen from '../screens/NewPasswordScreen';
import { NavigationContainer } from '#react-navigation/native'
import { createNativeStackNavigator } from '#react-navigation/native-stack';
import {Auth, Hub} from 'aws-amplify';
const Stack = createNativeStackNavigator();
const Navigation = () => {
const [user, setUser] = useState(undefined);
//Checking if user is already logged in or not!
const checkUser = async () => {
try {
const authUser = await Auth.currentAuthenticatedUser({bypassCache: true});
setUser(authUser);
} catch(e) {
setUser(null);
}
};
useEffect(() => {
checkUser();
}, []);
useEffect(() => {
const listener = data => {
if (data.payload.event === 'signIn' || data.payload.event === 'signOut') {
checkUser();
}
}
Hub.listen('auth', listener);
return () => Hub.remove('auth', listener);
}, []);
if (user === undefined) {
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<ActivityIndicator/>
</View>
);
}
return (
<NavigationContainer>
<Stack.Navigator>
{/*{If user is logged in navigate him to Homescreen else go throght the Screens based on the user selection */}
{user ? (
<Stack.Screen name='Home' component={HomeScreen}/>
) : (
<>
<Stack.Screen name='Login' component={LoginScreen}/>
<Stack.Screen name='Register' component={RegisterScreen}/>
<Stack.Screen name='ConfirmEmail' component={ConfirmEmailScreen}/>
<Stack.Screen name='ForgotPassword' component={ForgotPassword}/>
<Stack.Screen name='NewPassword' component={NewPasswordScreen}/>
</>
)}
</Stack.Navigator>
</NavigationContainer>
);
};
export default Navigation;
Here is the Login Screen:
import React, {useState} from 'react'
import { View, Text, Image, StyleSheet, useWindowDimensions, TouchableWithoutFeedback, Keyboard, Alert, KeyboardAvoidingView, ScrollView } from 'react-native'
import Logo from '../../../assets/images/logo-main.png'
import CustomButton from '../../components/CustomButton/CustomButton';
import CustomInput from '../../components/CustomInput/CustomInput';
import { useNavigation } from '#react-navigation/native';
import {Auth} from 'aws-amplify';
import {useForm} from 'react-hook-form';
const LoginScreen = () => {
const [loading, setLoading] = useState(false);
const {height} = useWindowDimensions();
const {control, handleSubmit, formState: {errors}} = useForm();
const navigation = useNavigation();
const onLoginPressed = async (data) => {
if(loading) {
return;
}
setLoading(true);
try {
await Auth.signIn(data.username, data.password);
navigation.navigate('Home');
} catch(e) {
Alert.alert('Opps', e.message)
}
setLoading(false);
};
const onForgotPasswordPressed = () => {
navigation.navigate('ForgotPassword');
}
const onRegisterPressed = () => {
navigation.navigate('Register')
}
return (
<KeyboardAvoidingView behavior={Platform.OS === "ios" ? "padding" : "height"} style={styles.container}>
<ScrollView contentContainerStyle={{flexGrow:1, justifyContent:'center'}} showsVerticalScrollIndicator={false}>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.root}>
<Image source={Logo} style={[styles.logo, {height : height * 0.2}]} resizeMode={'contain'} />
<CustomInput icon='user' name='username' placeholder='Username' control={control} rules={{required: 'Username is required'}} />
<CustomInput icon='lock' name='password' placeholder='Password' control={control} rules={{required: 'Password is required'}} secureTextEntry={true} />
<CustomButton text={loading ? 'Loading...' : 'Login Account'} onPress={handleSubmit(onLoginPressed)} />
<CustomButton text='Forgot Password?' onPress={onForgotPasswordPressed} type='TERTIARY' />
<CustomButton text="Don't have an account? Create one" onPress={onRegisterPressed} type='TERTIARY' />
</View>
</TouchableWithoutFeedback>
</ScrollView>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
root: {
alignItems: 'center',
padding: 20,
},
logo: {
width: 200,
maxWidth: 300,
maxHeight: 300,
},
});
export default LoginScreen;
Issue
At the time you are calling navigation.navigate('Home') there is no screen called in Home in Navigation because of that ternary that checks whether there is a user or not and renders conditionally your screens. That's why React Naviagation is not happy.
Solution
React Navigation's documention tell us that we shouldn't "manually navigate when conditionally rendering screens​". Means you should find a way to call setUser(user) in Login where setUser is the state setter from Navigation component.
To do so we can use a context and for that change Navigation as follow (I added comments where I changed things):
import React, { createContext, useEffect, useState } from "react";
import { ActivityIndicator, View } from "react-native";
import ConfirmEmailScreen from "../screens/ConfirmEmailScreen";
import ForgotPassword from "../screens/ForgotPassword";
import HomeScreen from "../screens/HomeScreen";
import LoginScreen from "../screens/LoginScreen";
import NewPasswordScreen from "../screens/NewPasswordScreen";
import RegisterScreen from "../screens/RegisterScreen";
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import { Auth, Hub } from "aws-amplify";
const Stack = createNativeStackNavigator();
// Line I added
export const AuthContext = createContext(null);
const Navigation = () => {
const [user, setUser] = useState(undefined);
//Checking if user is already logged in or not!
const checkUser = async () => {
try {
const authUser = await Auth.currentAuthenticatedUser({ bypassCache: true });
setUser(authUser);
} catch (e) {
setUser(null);
}
};
useEffect(() => {
checkUser();
}, []);
useEffect(() => {
const listener = (data) => {
if (data.payload.event === "signIn" || data.payload.event === "signOut") {
checkUser();
}
};
Hub.listen("auth", listener);
return () => Hub.remove("auth", listener);
}, []);
if (user === undefined) {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<ActivityIndicator />
</View>
);
}
return (
// Wrapper I added
<AuthContext.Provider value={{ user, setUser }}>
<NavigationContainer>
<Stack.Navigator>
{/*{If user is logged in navigate him to Homescreen else go throght the Screens based on the user selection */}
{user ? (
<Stack.Screen name="Home" component={HomeScreen} />
) : (
<>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="ConfirmEmail" component={ConfirmEmailScreen} />
<Stack.Screen name="ForgotPassword" component={ForgotPassword} />
<Stack.Screen name="NewPassword" component={NewPasswordScreen} />
</>
)}
</Stack.Navigator>
</NavigationContainer>
</AuthContext.Provider>
);
};
export default Navigation;
Consume AuthContext from Navigation in Login so you are able to call setUser after login, like below (I added comments where I changed thing):
import { useNavigation } from "#react-navigation/native";
import React, { useContext, useState } from "react";
import {
Alert,
Image,
Keyboard,
KeyboardAvoidingView,
ScrollView,
StyleSheet,
TouchableWithoutFeedback,
useWindowDimensions,
View,
} from "react-native";
import Logo from "../../../assets/images/logo-main.png";
import CustomButton from "../../components/CustomButton/CustomButton";
import CustomInput from "../../components/CustomInput/CustomInput";
import { Auth } from "aws-amplify";
import { useForm } from "react-hook-form";
// Line I added
import {AuthContext} from "./Navigation" // ⚠️ use the correct path
const LoginScreen = () => {
// Line I added
const { setUser } = useContext(AuthContext);
const [loading, setLoading] = useState(false);
const { height } = useWindowDimensions();
const {
control,
handleSubmit,
formState: { errors },
} = useForm();
const navigation = useNavigation();
const onLoginPressed = async (data) => {
if (loading) {
return;
}
setLoading(true);
try {
const user = await Auth.signIn(data.username, data.password);
// Line I added
setUser(user);
} catch (e) {
Alert.alert("Opps", e.message);
}
setLoading(false);
};
const onForgotPasswordPressed = () => {
navigation.navigate("ForgotPassword");
};
const onRegisterPressed = () => {
navigation.navigate("Register");
};
return (
<KeyboardAvoidingView
behavior={Platform.OS === "ios" ? "padding" : "height"}
style={styles.container}
>
<ScrollView
contentContainerStyle={{ flexGrow: 1, justifyContent: "center" }}
showsVerticalScrollIndicator={false}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View style={styles.root}>
<Image
source={Logo}
style={[styles.logo, { height: height * 0.2 }]}
resizeMode={"contain"}
/>
<CustomInput
icon="user"
name="username"
placeholder="Username"
control={control}
rules={{ required: "Username is required" }}
/>
<CustomInput
icon="lock"
name="password"
placeholder="Password"
control={control}
rules={{ required: "Password is required" }}
secureTextEntry={true}
/>
<CustomButton
text={loading ? "Loading..." : "Login Account"}
onPress={handleSubmit(onLoginPressed)}
/>
<CustomButton
text="Forgot Password?"
onPress={onForgotPasswordPressed}
type="TERTIARY"
/>
<CustomButton
text="Don't have an account? Create one"
onPress={onRegisterPressed}
type="TERTIARY"
/>
</View>
</TouchableWithoutFeedback>
</ScrollView>
</KeyboardAvoidingView>
);
};
const styles = StyleSheet.create({
root: {
alignItems: "center",
padding: 20,
},
logo: {
width: 200,
maxWidth: 300,
maxHeight: 300,
},
});

How to prevent logged in user to see login page

So I have a React Native app that I'm working on, and this is my authentication flow:
const Stack = createStackNavigator();
const MainContainer = () => {
const { currentUser } = useAuth();
if (currentUser) {
return <HamburgerStack />;
} else {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Login" component={LoginScreen} />
<Stack.Screen name="Register" component={RegisterScreen} />
<Stack.Screen name="ForgotPassword" component={ForgotPasswordScreen} />
</Stack.Navigator>
);
}
};
export default MainContainer;
I'm using firebase for authentication and currentUser is a Context hook.
When I get into the app and I'm already logged in there is one moment that the authentication pages are shown, probably because firebase is looking for the user's info on the device.
I cannot think of how to prevent the user to see them. Any tips?
Thank :)
What I did is created a separate stack for the dashboard, authentication, admin, and a splash screen (dummy) and wrapped all of them in a single stack.
MainStack.js
import React, { useEffect } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { StatusBar, View } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import AuthStack from './AuthStack';
import UserStack from './UserStack';
import AdminStack from './AdminStack';
import colors from '../constants/colors';
import Splash from '../screens/Splash';
export default function MainStack() {
const Stack = createStackNavigator();
return (
<SafeAreaProvider>
<NavigationContainer>
<View>
<StatusBar backgroundColor={colors.DarkGrey} />
</View>
<Stack.Navigator initialRouteName='Splash' screenOptions={{ animationEnabled: false }}>
<Stack.Screen name="Splash" component={Splash} options={{ headerShown: false }} />
<Stack.Screen name="AuthStack" component={AuthStack} options={{ headerShown: false }} />
<Stack.Screen name="UserStack" component={UserStack} options={{ headerShown: false }} />
<Stack.Screen name="AdminStack" component={AdminStack} options={{ headerShown: false }} />
</Stack.Navigator>
</NavigationContainer>
</SafeAreaProvider>
);
};
AuthStack.js
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import Login from '../screens/AuthScreens/Login';
import Signup from '../screens/AuthScreens/Signup';
export default function AuthStack() {
const Stack = createStackNavigator();
return (
<Stack.Navigator initialRouteName='Login' screenOptions={{ animationEnabled: false }}>
<Stack.Screen name="Login" component={Login} options={{ headerShown: false }} />
<Stack.Screen name="Signup" component={Signup} options={{ headerShown: false }} />
</Stack.Navigator>
);
};
UserStack.js (Dashboard)
import React from 'react';
import { createStackNavigator } from '#react-navigation/stack';
import TabNavigator from './TabNavigator/Index';
import EditProfile from '../screens/UserScreens/EditProfile';
import CarDetails from '../screens/UserScreens/CarDetails';
import Checkout from '../screens/UserScreens/Checkout';
import SelectCar from '../screens/UserScreens/SelectCar';
import BookingDetails from '../screens/UserScreens/BookingDetails';
export default function UserStack() {
const Stack = createStackNavigator();
return (
<Stack.Navigator initialRouteName='TabNavigator' screenOptions={{ animationEnabled: false }}>
<Stack.Screen name="TabNavigator" component={TabNavigator} options={{ headerShown: false }} />
<Stack.Screen name="EditProfile" component={EditProfile} options={{ headerShown: false }} />
<Stack.Screen name="SelectCar" component={SelectCar} options={{ headerShown: false }} />
<Stack.Screen name="CarDetails" component={CarDetails} options={{ headerShown: false }} />
<Stack.Screen name="Checkout" component={Checkout} options={{ headerShown: false }} />
<Stack.Screen name="BookingDetails" component={BookingDetails} options={{ headerShown: false }} />
</Stack.Navigator>
);
};
Splash.js (It's a screen with no UI)
in this, I had to get data from the database so it's like that you can directly check for user and navigate
import React, { useEffect } from "react";
import { firebase } from "#react-native-firebase/auth"
import database from '#react-native-firebase/database';
import SplashScreen from 'react-native-splash-screen'
export default function Splash({ navigation }) {
useEffect(() => {
let user = firebase.auth().currentUser;
// console.log('currentUser: ',user);
if (user) {
database()
.ref('/Users/' + user.uid)
.once('value')
.then(snapshot => {
let Data = snapshot.val();
// console.log(Data);
if (Data.Role == 'ADMIN') {
navigation.replace('AdminStack');
SplashScreen.hide();
console.log("Success:", "Admin Logged In successfully");
} else {
navigation.replace('UserStack');
SplashScreen.hide();
console.log("Success:", "User Logged In successfully");
}
})
} else {
navigation.replace('AuthStack')
SplashScreen.hide();
}
}, [])
return null;
}

How to make react-native navigation screen fill up ios simulator screen

I have a problem with react native navigation screen
Here is my problem picture:
enter image description here
As you can see the navigation screen is shown smaller than the simulator screen size.
Here is my related code
Navigation code:
import * as React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import Ionicons from 'react-native-vector-icons/Ionicons';
import {Text, Dimensions} from 'react-native';
import HomeScreen from '../screens/HomeScreen';
import AboutScreen from '../screens/AboutScreen';
import ProfileScreen from '../screens/ProfileScreen';
const fullScreenWidth = Dimensions.get('window').width;
const Stack = createStackNavigator();
function HomeStackScreen() {
return (
<Stack.Navigator>
<Stack.Screen name="HomeScreen" component={HomeScreen} />
</Stack.Navigator>
);
}
function AboutStackScreen() {
return (
<Stack.Navigator>
<Stack.Screen name="AboutScreen" component={AboutScreen} />
</Stack.Navigator>
);
}
function ProfileStackScreen() {
return (
<Stack.Navigator>
<Stack.Screen name="ProfileScreen" component={ProfileScreen} />
</Stack.Navigator>
);
}
const Tab = createBottomTabNavigator();
function Navigation(props) {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({route}) => ({
headerTitle: () => {
return <Text>Header</Text>;
},
tabBarIcon: ({focused, color, size, padding}) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'About') {
iconName = focused
? 'information-circle'
: 'information-circle-outline';
} else if (route.name === 'Profile') {
iconName = focused ? 'person' : 'person-outline';
}
return (
<Ionicons
name={iconName}
size={size}
color={color}
style={{paddingBottom: padding}}
/>
);
},
tabBarActiveTintColor: 'lightseagreen',
tabBarInactiveTintColor: 'grey',
tabBarLableStyle: {fontSize: 16},
tabBarstyle: {width: fullScreenWidth},
})}>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="About" component={AboutStackScreen} />
<Tab.Screen name="Profile" component={ProfileStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default Navigation;
Screen code(each screen has same format):
import React from 'react';
import {SafeAreaView, Text, StyleSheet} from 'react-native';
const HomeScreen = props => {
return (
<SafeAreaView style={styles}>
<Text>Home Screen</Text>
</SafeAreaView>
);
};
export default HomeScreen;
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
App screen:
import 'react-native-gesture-handler';
import * as React from 'react';
import {useState, useEffect} from 'react';
import {Text, View, StyleSheet, Button, Linking} from 'react-native';
import Amplify, {Auth, Hub} from 'aws-amplify';
import awsconfig from './src/aws-exports';
import {Authenticator, withOAuth} from 'aws-amplify-react-native';
import InAppBrowser from 'react-native-inappbrowser-reborn';
import SignUp from './src/components/auth/SignUp';
import ConfirmSignUp from './src/components/auth/ConfirmSignUp';
import SignIn from './src/components/auth/SignIn';
import ForgotPassword from './src/components/auth/ForgotPassword';
import ChangePassword from './src/components/auth/ChangePassword';
import Navigation from './src/components/navigation/Navigation';
import UserContext from './src/components/userContext/UserContext';
async function urlOpener(url, redirectUrl) {
await InAppBrowser.isAvailable();
const {type, url: newUrl} = await InAppBrowser.openAuth(url, redirectUrl, {
showTitle: false,
enableUrlBarHiding: true,
enableDefaultShare: false,
ephemeralWebSession: false,
});
if (type === 'success') {
Linking.openURL(newUrl);
}
}
Amplify.configure({
...awsconfig,
Analytics: {
disabled: true,
},
oauth: {
...awsconfig.oauth,
urlOpener,
},
});
function Home(props) {
return (
<View>
<Navigation {...props} />
<Button title="Sign Out" onPress={() => Auth.signOut()} />
</View>
);
}
const AuthScreens = props => {
console.log(props.authState);
switch (props.authState) {
case 'signIn':
return <SignIn {...props} />;
case 'signUp':
return <SignUp {...props} />;
case 'forgotPassword':
return <ForgotPassword {...props} />;
case 'confirmSignUp':
return <ConfirmSignUp {...props} />;
case 'changePassword':
return <ChangePassword {...props} />;
case 'signedIn':
return <Home />;
case 'verifyContact':
return <Home />;
default:
return <></>;
}
};
const App = props => {
const [user, setUser] = useState({});
useEffect(() => {
Hub.listen('auth', ({payload: {event, data}}) => {
switch (event) {
case 'signIn':
case 'cognitoHostedUI':
getUser().then(userData => setUser(userData));
break;
case 'signOut':
setUser(null);
break;
case 'signIn_failure':
case 'cognitoHostedUI_failure':
console.log('Sign in failure', data);
break;
}
});
getUser().then(userData => setUser(userData));
}, []);
function getUser() {
return Auth.currentAuthenticatedUser()
.then(userData => userData)
.catch(() => console.log('Not signed in'));
}
const {googleSignIn, facebookSignIn} = props;
return (
<View style={styles.container}>
<Authenticator
usernameAttributes="email"
hideDefault={true}
authState="signIn">
<AuthScreens />
</Authenticator>
{!user && (
<View style={{marginBottom: 200}}>
<Text style={{textAlign: 'center'}}> - OR - </Text>
<Button title="Login with Google" onPress={googleSignIn} />
<Button title="Login with Facebook" onPress={facebookSignIn} />
</View>
)}
</View>
);
};
export default withOAuth(App);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
});
How to fix this to fill up simulator screen?
I solve this problem by myself.
Actually the problem is in tabBarStyle!!!!
This is fixed code in Navigation.js:
import * as React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import Ionicons from 'react-native-vector-icons/Ionicons';
import {Text, Dimensions} from 'react-native';
import HomeScreen from '../screens/HomeScreen';
import AboutScreen from '../screens/AboutScreen';
import ProfileScreen from '../screens/ProfileScreen';
const fullScreenWidth = Dimensions.get('window').width;
const Stack = createStackNavigator();
function HomeStackScreen() {
return (
<Stack.Navigator>
<Stack.Screen name="HomeScreen" component={HomeScreen} />
</Stack.Navigator>
);
}
function AboutStackScreen() {
return (
<Stack.Navigator>
<Stack.Screen name="AboutScreen" component={AboutScreen} />
</Stack.Navigator>
);
}
function ProfileStackScreen() {
return (
<Stack.Navigator>
<Stack.Screen name="ProfileScreen" component={ProfileScreen} />
</Stack.Navigator>
);
}
const Tab = createBottomTabNavigator();
function Navigation(props) {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({route}) => ({
headerTitle: () => {
return <Text>Header</Text>;
},
tabBarIcon: ({focused, color, size, padding}) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'About') {
iconName = focused
? 'information-circle'
: 'information-circle-outline';
} else if (route.name === 'Profile') {
iconName = focused ? 'person' : 'person-outline';
}
return (
<Ionicons
name={iconName}
size={size}
color={color}
style={{paddingBottom: padding}}
/>
);
},
tabBarActiveTintColor: 'lightseagreen',
tabBarInactiveTintColor: 'grey',
tabBarLableStyle: {fontSize: 16},
tabBarStyle: {width: fullScreenWidth},
})}>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="About" component={AboutStackScreen} />
<Tab.Screen name="Profile" component={ProfileStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
export default Navigation;

Categories