at using Expo, After splash screen, Blink(Flash) with white screen - react-native

I made Expo app with react native navigation 5.
please refer my solved question(same install situation).
but After splash on FIRST launching, White screen is blink(Flashed : about 0.5 sec). Especially, At Dark theme, I can find this bug easily.
This is App.tsx
import { StatusBar } from 'expo-status-bar';
import React from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import useCachedResources from './hooks/useCachedResources';
import useColorScheme from './hooks/useColorScheme';
import Navigation from './navigation';
export default function App() {
const isLoadingComplete = useCachedResources();
const colorScheme = useColorScheme();
if (!isLoadingComplete) {
return null;
} else {
return (
<SafeAreaProvider>
<Navigation colorScheme={colorScheme} />
<StatusBar style='light'/>
</SafeAreaProvider>
);
}
}
and this is index.tsx at nvigation
import { RootStackParamList } from '../types';
import BottomTabNavigator from './BottomTabNavigator';
import LinkingConfiguration from './LinkingConfiguration';
export default function Navigation({ colorScheme }: { colorScheme: ColorSchemeName }) {
return (
<NavigationContainer
linking={LinkingConfiguration}
theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<RootNavigator />
</NavigationContainer>
);
}
const Stack = createStackNavigator<RootStackParamList>();
function RootNavigator() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Root" component={BottomTabNavigator} />
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />
</Stack.Navigator>
);
}
At Hook, useCachedResources.ts
import { MaterialIcons } from '#expo/vector-icons';
import * as Font from 'expo-font';
import * as SplashScreen from 'expo-splash-screen';
import * as React from 'react';
export default function useCachedResources() {
const [isLoadingComplete, setLoadingComplete] = React.useState(false);
React.useEffect(() => {
async function loadResourcesAndDataAsync() {
try {
SplashScreen.preventAutoHideAsync();
await Font.loadAsync({
...MaterialIcons.font,
'space-mono': require('../assets/fonts/SpaceMono-Regular.ttf'),
});
} catch (e) {
console.warn(e);
} finally {
setLoadingComplete(true);
SplashScreen.hideAsync();
}
}
loadResourcesAndDataAsync();
}, []);
return isLoadingComplete;
}
and useColorScheme.ts
import { Appearance, ColorSchemeName } from 'react-native';
import { useEffect, useRef, useState } from 'react';
export default function useColorScheme(delay = 500): NonNullable<ColorSchemeName> {
const [colorScheme, setColorScheme] = useState(Appearance.getColorScheme());
let timeout = useRef<NodeJS.Timeout | null>(null).current;
useEffect(() => {
Appearance.addChangeListener(onColorSchemeChange);
return () => {
resetCurrentTimeout();
Appearance.removeChangeListener(onColorSchemeChange);
};
}, []);
function onColorSchemeChange(preferences: Appearance.AppearancePreferences) {
resetCurrentTimeout();
timeout = setTimeout(() => {
setColorScheme(preferences.colorScheme);
}, delay);
}
function resetCurrentTimeout() {
if (timeout) {
clearTimeout(timeout);
}
}
return colorScheme as NonNullable<ColorSchemeName>;
}
How to solve this bug? Please give me your hand.

I just solved this issue by specifying backgroundColor in my app.json to match the background color of the splash screen.
Expo Docs for backgroundColor
(string) - The background color for your app, behind any of your React views. This is also known as the root view background color.
6 character long hex color string, for example, '#000000'. Default is white: '#ffffff'.

Try to use SplashScreen.preventAutoHideAsync() in your App.js before its declaration.
For example:
SplashScreen.preventAutoHideAsync();
export default function App() {
...
Don't forget to delete the SplashScreen.preventAutoHideAsync() call in your loadResourcesAndDataAsync() function. Keep it only in the App.js file.
I hope this help you. Worked for me.

Just got a breakthrough on this after few days so what you will modify in your index.ts file
your code
function RootNavigator() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Root" component={BottomTabNavigator} />
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title:
'Oops!' }} />
</Stack.Navigator>
);
}
what it should be :
function RootNavigator() {
return (
<Stack.Navigator initialRouteName="Root" screenOptions={{ headerShown: false
}}>
<Stack.Screen name="Root" component={BottomTabNavigator} />
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title:
'Oops!' }} />
</Stack.Navigator>
);
}
by adding initialRouteName your problem should get solved

their other reason also makes sure that your import {useState,useEffect } or other hooks from react not react.development, you will face that if you published your app on the expo or generated API or apk

Related

Fail Importing expo font

I'm getting this error when trying to import a font with expo font.
null is not an object" (evaluating 'dispatcher.useState')
But the error is for sure related to my font import.
import { NavigationContainer, useNavigation } from '#react-navigation/native';
import { FontAwesome } from '#expo/vector-icons';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import {createStackNavigator} from '#react-navigation/stack';
import { AuthContext } from "./context";
import BottomTabNavigator from "./navigation/tabsNav";
import * as firebase from "firebase";
import Myapp from './instanceFirebase';
import AuthStackScreen from './navigation/authStack';
import { ActivityIndicator } from 'react-native'
import FlashMessage from "react-native-flash-message";
const instanceFirebase = Myapp;
import "firebase/functions";
import "firebase/firestore";
import * as Font from 'expo-font';
console.log("font", Font)
const [fontState, setFontState] = React.useState(false);
const MainStack = createStackNavigator();
const MainStackScreen = () => {
return(
<MainStack.Navigator initialRouteName="mainPage" >
<MainStack.Screen
name="mainPage"
component={BottomTabNavigator} />
</MainStack.Navigator>
)
}
const RootStack = createStackNavigator();
const RootStackScreen = ({ userToken }) => (
<RootStack.Navigator headerMode="none">
{userToken ? (
<RootStack.Screen
name="App"
component={MainStackScreen}
/>
) : (
<RootStack.Screen
headerMode="none"
name="Authstack"
component={AuthStackScreen}
options={{
animationEnabled: false
}}
/>
)}
</RootStack.Navigator>
);
export default function token() {
const [userToken, setUserToken] = React.useState(null);
const Context = {
instanceFirebase: instanceFirebase,
signOut: () => {
setUserToken(null);
},
signIn:() => {
setUserToken(true);
}
}
React.useEffect(() => {
const fetchFont = async () => {
await Font.loadAsync({
Jost: require('./assets/fonts/Jost-Regular.ttf'),
Jost_black: require('./assets/fonts/Jost-Black.ttf')
})
setFontState(true)
}
fetchFont()
firebase.auth().onAuthStateChanged(user => {
if(user){
setUserToken(true)
} else {
setUserToken(null)
}
})
}, [])
if (!fontState) {
return <ActivityIndicator />
} else {
return (
<AuthContext.Provider value={instanceFirebase}>
<NavigationContainer>
<RootStackScreen userToken={userToken} />
<FlashMessage position="top" style={{zIndex:99, flex:1, marginTop:30}}/>
</NavigationContainer>
</AuthContext.Provider>
)
}
}
I checked that expo-font is well import, and it is fine.
I don't understand what is happening, when i comment the code related to the import of the font my app just work. So my error is for sure related to the font import.
I do things correctly ?
Thanks per advance for your help
The right answer from Med El Mobarik:
Indeed i am not supposed to use the useState outside a funcionnal component
const [fontState, setFontState] = React.useState(false);
Nevertheless is there a way to load the font in the root page (at the start of the app/Where the instance of my app is launching) ? I want to avoid to load the font on each page.

undefined is not an object react navigation from doc

I have this code that come directly from the doc of react navigation link here.
import * as React from 'react';
import { Button } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
const Stack = createNativeStackNavigator();
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
const HomeScreen = ({ navigation }) => {
return (
<Button
title="Go to Jane's profile"
onPress={() =>
navigation.navigate('Profile', { name: 'Jane' })
}
/>
);
};
const ProfileScreen = ({ navigation, route }) => {
return <Text>This is {route.params.name}'s profile</Text>;
};
const App = () => {
return (
<NavigationContainer>
<HomeScreen />
</NavigationContainer>
);
};
export default App;
and when i click on the button i have this error : undefined is not an object. can you help me found what's wrong ?
That just to test at the end i want different page in my app and navigate into,
i don't understant why all the different page is on the same file ...
Thank you,
have good coding day
The Reason Behind this is you are used HomeScreen component inside App component.You Have to use MyStack component inside the App component.
then you get error about NavigationContainer you have to use NavigationContainer one time not in nested.Please remove NavigationContainer from App component or MyStack component.
also Add ..
import{Text} from 'react-native';
congratulations Your Programm run perfectly.
I Changed small things in your code please copy and then try it will work.
import * as React from 'react';
import { Button,Text} from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
const Stack = createNativeStackNavigator();
const MyStack = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen
name="Home"
component={HomeScreen}
options={{ title: 'Welcome' }}
/>
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
};
const HomeScreen = ({ navigation }) => {
return (
<Button
title="Go to Jane's profile"
onPress={() =>
navigation.navigate('Profile', { name: 'Jane' })
}
/>
);
};
const ProfileScreen = ({ navigation, route }) => {
return <Text>This is {route.params.name}'s profile</Text>;
};
const App = () => {
return (
<MyStack />
);
};
export default App;
Happy Coding

React Native React Navigation screen does not redirect after login with AsyncStorage

So I built a login system with React Native, React Navigation and AsyncStorage. If the user clicks on a button he gets logged in and the value of the AsyncStorage Key "#loginuser" gets refreshed. Now I expected
that the screen automatically gets refreshed, but I have to close the App and start it again - This is not optimal.
(I also saw React-Native/React navigation redirection after login with redux post, but it is very old)
App.js
import React from 'react';
import { Text, View, StyleSheet, Button, TouchableOpacity, TextInput } from 'react-native';
import DefaultStackNavigation from './components/Navigation/Navigation';
const App = () => {
return(
<View>
<DefaultStackNavigation />
</View>
)
}
export default App;
Navigation.js
import React, {useEffect, useState} from 'react';
//React Native
import { Text, View, StyleSheet} from 'react-native';
//Screens
import HomeScreen from '../HomeScreen/HomeScreen'
import AddScreen from "../AddScreen/AddScreen";
import NotificationScreen from "../NotificationScreen/NotificationScreen";
import MenuScreen from "../MenuScreen/MenuScreen";
import SearchScreen from "../SearchScreen/SearchScreen";
import PostJobScreen from "../PostJobScreen/PostJobScreen";
import JobOfferScreen from "../JobOfferScreen/JobOfferScreen";
import ProfileScreen from "../ProfileScreen/ProfileScreen";
import NoneLoggedinScreen from "../NoneLoggedinScreen/NoneLoggedinScreen"
import SignupModal from "../NoneLoggedinScreen/SignupModal"
//React Navigation
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
//Third Party
import AsyncStorage from '#react-native-async-storage/async-storage';
const Tab = createBottomTabNavigator();
//Tab bar
const HomeTabs = () => {
return (
<Tab.Navigator tabBarOptions={{style:{height: 50}}, {showIcon: true}, {showLabel: false}} >
<Tab.Screen name="Home" component={HomeScreen}/>
<Tab.Screen name="SearchScreen" component={SearchJobStack}/>
<Tab.Screen name="AddScreen" component={AddScreen}/>
<Tab.Screen name="NotificationScreen" component={NotificationScreen} />}}/>
<Tab.Screen name="MenuScreen" component={MenuScreen}/>}}/>
</Tab.Navigator>
);
}
const Stack = createStackNavigator();
const STORAGE_KEY = '#loginStatus'
const DefaultStackNavigation = () => {
const [loginStatus, setLoginStatus] = useState()
const readData = async () => {
try {
const isLoggedIn = JSON.parse(await AsyncStorage.getItem(STORAGE_KEY))
console.log(isLoggedIn)
if (isLoggedIn !== null) {
setLoginStatus(isLoggedIn)
}
} catch (e) {
alert('Failed to fetch the data from storage')
}
}
readData()
return loginStatus ? (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} independent={false}>
<Stack.Screen name="HomeTabs" component={HomeTabs} />
<Stack.Screen name="PostJobScreen" component={PostJobScreen} />
<Stack.Screen name="ProfileScreen" component={ProfileScreen}/>
</Stack.Navigator>
</NavigationContainer>
) : (
<NavigationContainer>
<Stack.Navigator screenOptions={{headerShown: false}} independent={false}>
<Stack.Screen name="NoneLoggedinScreen" component={NoneLoggedinScreen} />
<Stack.Screen name="SignupModal" component={SignupModal} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default DefaultStackNavigation;
NoneLoggedinScreen.js
import React, { useState, useEffect } from 'react';
import { Text, View, Button} from 'react-native';
import AsyncStorage from '#react-native-async-storage/async-storage';
const STORAGE_KEY = '#loginStatus'
const SignupModal = () => {
const [loginStatus, setLoginStatus] = useState(false)
const saveData = async (parmLoginStatus) => {
try {
await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(parmLoginStatus))
alert('Data successfully saved -> Logged In')
console.log(loginStatus)
} catch (e) {
alert('Failed to save the data to the storage')
}
}
const onSubmitLogin = () => {
setLoginStatus(true)
saveData(true)
}
return(
<View>
<Text>Login Page. Press the button to log in and stay logged in</Text>
<Button title="Log in" onPress={() => onSubmitLogin()}/>
</View>
);
};
const styles = StyleSheet.create({
App: {
flex: 1,
backgroundColor: "white"
},
});
export default SignupModal;
Now I was expecting the page to reload and I can use the app. Unfortunately it doesn't. The data is saved and the login status is set to true, but I have to restart the app to use it. The perfidious thing is, if I write the AsyncStorage login logic from the NoneLoggedinScreen.js file into the App.js file, the app works fine -> However, this is not an alternative for me, because the general structure of the app is, I think, built relatively sensibly.
Also when the user tries to be redirected manually (by button) with navigation.navigate("HomeTabs") after logging in doesn't work, and I get an error that Home doesn't exist, which is also not understandable, because the navigation has actually been set to Logged in now. Has anyone ever had this problem?
This are my dependencies by the way
"dependencies": {<br>
"#react-native-async-storage/async-storage": "^1.15.1",<br>
"#react-native-community/masked-view": "^0.1.10",<br>
"#react-navigation/bottom-tabs": "^5.11.7",<br>
"#react-navigation/native": "^5.9.2",<br>
"#react-navigation/stack": "^5.14.2",<br>
"react": "16.13.1",<br>
"react-native": "0.63.4",<br>
"react-native-gesture-handler": "^1.10.1",<br>
"react-native-reanimated": "^1.13.2",<br>
"react-native-safe-area-context": "^3.1.9",<br>
"react-native-screens": "^2.17.1",<br>
}
In your authentication strategy, there is a hidden assumption that when you save data using AsyncStorage in NoneLoggedInScreen, the data will immediately be read using the readData function in DefaultStackNavigation. The reason why this does not happen is that readData is only called when DefaultStackNavigation renders/re-renders. This will not happen when one of its children sets some data in local storage.
There are many ways to fix this. Using redux with redux-persist or another state-management and persistence setup, or a purpose-built context-based solution (see eg. this Kent C. Dodds article on Authentication in React Applications).
The second, minor issue that you mention:
navigation.navigate("HomeTabs") after logging in doesn't work, and I get an error that Home doesn't exist
does make sense, and the reason behind it is that you conditionally render two different NavigationContainers. They don't know about each others routes, so you can't navigate between them.
To fix this, you should render one NavigationContainer and conditionally render one of the Stack.Navigators as its child.
To manage authentication flow in a smooth way we should make two seperate navigators for the navigation. I find this workflow the best as of now. It works perfectly and is clean.
So what I usually do in my Projects that I create two navigators AuthNavigator.js and AppNavigator.js and then use conditional rendering.
So what you can do is create a folder called navigation where your App.js is located. Then inside navigation folder create two files called AppNavigator.js and AuthNavigator.js.
Your AuthNavigator.js should look like this
import React from "react";
import NoneLoggedinScreen from "../NoneLoggedinScreen/NoneLoggedinScreen";
import SignupModal from "../NoneLoggedinScreen/SignupModal";
import { createStackNavigator } from "#react-navigation/stack";
const Stack = createStackNavigator();
const AuthNavigator = () => {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} independent={false}>
<Stack.Screen name="NoneLoggedinScreen" component={NoneLoggedinScreen} />
<Stack.Screen name="SignupModal" component={SignupModal} />
</Stack.Navigator>
);
};
export default AuthNavigator;
Your AppNavigator.js should look like this
import React from "react";
import HomeScreen from "../HomeScreen/HomeScreen";
import AddScreen from "../AddScreen/AddScreen";
import NotificationScreen from "../NotificationScreen/NotificationScreen";
import MenuScreen from "../MenuScreen/MenuScreen";
import PostJobScreen from "../PostJobScreen/PostJobScreen";
import ProfileScreen from "../ProfileScreen/ProfileScreen";
import { createStackNavigator } from "#react-navigation/stack";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
const Tab = createBottomTabNavigator();
//Tab bar
const HomeTabs = () => {
return (
<Tab.Navigator
tabBarOptions={
({ style: { height: 50 } }, { showIcon: true }, { showLabel: false })
}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="SearchScreen" component={SearchJobStack} />
<Tab.Screen name="AddScreen" component={AddScreen} />
<Tab.Screen name="NotificationScreen" component={NotificationScreen} />
<Tab.Screen name="MenuScreen" component={MenuScreen} />
</Tab.Navigator>
);
};
const Stack = createStackNavigator();
const AppNavigator = () => {
return (
<Stack.Navigator screenOptions={{ headerShown: false }} independent={false}>
<Stack.Screen name="HomeTabs" component={HomeTabs} />
<Stack.Screen name="PostJobScreen" component={PostJobScreen} />
<Stack.Screen name="ProfileScreen" component={ProfileScreen} />
</Stack.Navigator>
);
};
export default AppNavigator;
And your App.js should look like this
import React, { useState, useEffect } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import AuthStorage from './auth/storage';
import AppNavigator from './navigation/AppNavigator';
import AuthNavigator from './navigation/AuthNavigator';
const App = () => {
const [loginStatus, setLoginStatus] = useState(false);
// I am using useEffect hook but I would prefer using `AppLoading` to restore token if it exists
useEffect(() => {
readData();
}, []);
const readData = async () => {
try {
const isLoggedIn = JSON.parse(await AuthStorage.getItem());
console.log(isLoggedIn);
if (isLoggedIn !== null) {
setLoginStatus(isLoggedIn);
}
} catch (e) {
alert('Failed to fetch the data from storage');
}
};
return (
<NavigationContainer>
{loginStatus ? <AppNavigator /> : <AuthNavigator />}
</NavigationContainer>
);
};
export default App;
Create a folder called auth where your App.js is located. Inside that create a file called storage.js. Now inside storage.js paste this code
import AsyncStorage from '#react-native-async-storage/async-storage';
const AuthToken = '#loginStatus';
const storeItem = async (value) => {
try {
await AsyncStorage.setItem(AuthToken, JSON.stringify(value));
return true;
} catch (e) {
return false;
}
};
const getItem = async () => {
try {
const jsonValue = await AsyncStorage.getItem(AuthToken);
return jsonValue != null ? JSON.parse(jsonValue) : null;
} catch (e) {
return null;
}
};
export default { storeItem, getItem };

React-native stack navigation not showing right screen

all, I am having a problem that my stack navigation is not showing any screen.
I have 'signup' and 'login' screens, and I build a stack navigator(which is the startStack) try to navigate between those two screens. But it doesn't work. when a user first come(if she hasn't logged in before), the 'login' screen should be shown.
Please help me out , I really appreciate , thank you.
this is the App.js
import React, { Component } from "react";
import "react-native-gesture-handler";
import { StyleSheet, Text, View } from "react-native";
import Login from "./screens/login";
import Signup from "./screens/signup";
import MyOrders from "./screens/myOrders";
import firebase from "firebase";
import StartNavigator from "./routes/startStack";
class App extends Component {
state = {
loggedIn: null,
};
componentDidMount() {
var firebaseConfig = {
apiKey: "AIzaSyASR4GAXSRGsiDhOTF_UsdqHzqHYcHPk_U",
authDomain: "onlineorderportal-68a8f.firebaseapp.com",
databaseURL: "https://onlineorderportal-68a8f.firebaseio.com",
projectId: "onlineorderportal-68a8f",
storageBucket: "onlineorderportal-68a8f.appspot.com",
messagingSenderId: "658149255251",
appId: "1:658149255251:web:37940844cdc5403e173ea6",
measurementId: "G-KGQ1F2F3WE",
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
} else {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.setState({ loggedIn: true });
} else {
this.setState({ loggedIn: false });
}
});
}
}
renderContent = () => {
switch (this.state.loggedIn) {
case false:
console.log("false case login");
// return <Login />;
// return <Signup />;
return <StartNavigator />;
case true:
console.log("true case Navigator");
return <MyOrders />;
}
};
render() {
return (
<View style={styles.container}>
{this.renderContent()}
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#FFFFFF",
alignItems: "center",
justifyContent: "center",
},
});
export default App;
This is the startStack.js
import React from "react";
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from "#react-navigation/native";
import Login from "../screens/login";
import Signup from "../screens/signup";
const Stack = createStackNavigator();
function StartNavigator() {
console.log("enter Start");
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
<Stack.Screen name="Signup" component={Signup} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default StartNavigator;
When I try to return 'Login' or 'Signup' separately, it correctly show the pages separately.
Thank you so much !!!
You have created StartNavigator, which has only two screens 'Login' and 'Singup'. And you have set initialRouteName as 'Login'. So your 'App' component never called. So all the code that you written on App component never run. To work this add App component in your StartNavigator and set initialRouteName to app like below :
import React from "react";
import { createStackNavigator } from "#react-navigation/stack";
import { NavigationContainer } from "#react-navigation/native";
import Login from "../screens/login";
import Signup from "../screens/signup";
import App from "../app/path"; // import App here
const Stack = createStackNavigator();
function StartNavigator() {
console.log("enter Start");
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Login">
<Stack.Screen name="App" component={App} /> // add app component
<Stack.Screen name="Signup" component={Signup} />
<Stack.Screen name="Login" component={Login} />
</Stack.Navigator>
</NavigationContainer>
);
}
export default StartNavigator;
Did you check conditions after receive data from the server?
what is the value of 'loggedIn'?
I think that the value of 'loggedIn' is not correct or your switch case is not working.
use console.log before doing any condition for sure that the value of 'loggedIn' is correct.

switchNavigator with react-navigation 5

With react-navigation 4, I was able to import and use switchNavigator from "react-navigation" package.
import {
createAppContainer,
createSwitchNavigator,
createStackNavigator
} from "react-navigation";
import MainTabNavigator from "./MainTabNavigator";
import LoginScreen from "../screens/LoginScreen";
import AuthLoadingScreen from "../screens/AuthLoadingScreen";
export default createAppContainer(
createSwitchNavigator(
{
App: MainTabNavigator,
Auth: AuthLoadingScreen,
Login: createStackNavigator({ Login: LoginScreen })
},
{
initialRouteName: "Auth"
}
)
);
With react-navigation 5, I don't see the createSwitchNavigator in the package anymore. The documentation isn't helpful either. Is the use now not recommended? My use case is to show login screen before user is logged in and switch to the app after user logs in. React-navigation has given an example of authentication flow but is it possible to use switchNavigator - which seems much simpler.
The switchNavigator was removed because you can do the exact same stuff now with the help of rendering components conditionally.
import React from 'react';
import {useSelector} from "react-redux";
import {NavigationContainer} from "#react-navigation/native";
import { AuthNavigator, MyCustomNavigator } from "./MyCustomNavigator";
const AppNavigator = props => {
const isAuth = useSelector(state => !!state.auth.access_token);
return (
<NavigationContainer>
{ isAuth && <MyCustomNavigator/>}
{ !isAuth && <AuthNavigator/>}
</NavigationContainer>
);
};
export default AppNavigator;
The lines inside the NavigationContainer fully replace the old switch navigator.
I had also used SwitchNavigator of Navigator 4 then after migrating other pages to Navigator 5, i tried to move authentication part to Navigator 5. I could not achieve SwitchNavigator functionality using Navigator 5. Then decided to use "Compatibility layer" provided in Navigation API 5. https://reactnavigation.org/docs/5.x/compatibility
Hope below code will useful for you.
import { createStackNavigator } from '#react-navigation/stack'
import { NavigationContainer } from '#react-navigation/native';
import { createSwitchNavigator } from "#react-navigation/compat";
import { createCompatNavigatorFactory } from '#react-navigation/compat'
const AppStack = createCompatNavigatorFactory(createStackNavigator)(
{ screen: Home },
{headerMode:'none'}
);
const AuthStack = createCompatNavigatorFactory(createStackNavigator)({ screen:Login });
const SwitchNavigator= createSwitchNavigator(
{
Starter: AuthValidation,
App: AppStack,
Auth: AuthStack
},
{
initialRouteName:'Starter'
}
);
export default function App (){
return(
<NavigationContainer>
<SwitchNavigator/>
</NavigationContainer>
);
}
Here AuthValidation validate for token and depending on value it navigate to "Login" or "Home" Page
_checkAuthetication = async() =>{
const isUserAuthenticated= await AsyncStorage.getItem("isAuthenticated");
this.props.navigation.navigate(isUserAuthenticated ? 'App':'Auth');
}
Hey there is no switch navigator in react navigation 5, however you can do this or something on the same lines:
import React, { useEffect } from 'react'
import { StyleSheet, Text, View, ActivityIndicator } from 'react-native'
import { NavigationContainer } from "#react-navigation/native";
import BottomTabsNavigator from './BottomTabsNavigator'
import AccountNavigator from './AccountNavigator'
import firebase from '../api/config'
const SwitchNavigator = ({navigation}) => {
useEffect(() => {
firebase.auth().onAuthStateChanged(user => {
navigation.navigate(user ? "BottomTabsNavigator" : "AccountNavigator")
})
}, [])
return (
<View style={styles.container}>
<Text>Loading...</Text>
<ActivityIndicator size="large" color="#e9446a"></ActivityIndicator>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
export default SwitchNavigator
and then a Stack Navigator :
import React from 'react'
import { createStackNavigator } from '#react-navigation/stack'
import BottomTabsNavigator from './BottomTabsNavigator'
import AccountNavigator from './AccountNavigator'
import SwitchNavigator from './SwitchNavigator'
import { NavigationContainer } from "#react-navigation/native";
const StackApp = createStackNavigator()
export default function Stack() {
return (
<NavigationContainer>
<StackApp.Navigator initialRouteName='Loading' headerMode='none'>
<StackApp.Screen name='Loading' component={SwitchNavigator} />
<StackApp.Screen name='AccountNavigator' component={AccountNavigator}/>
<StackApp.Screen name='BottomTabsNavigator' component={BottomTabsNavigator}/>
</StackApp.Navigator>
</NavigationContainer>
)
}
and then import the Stack navigator into your app.js file like this:
export default App = () => ( <Stack /> )
This is w.r.t to above query
[Then how could we later navigate from a "LoginScreen" (inside
AuthNavigator ) to "HomeScreen" (inside MyCustomNavigator )? – TalESid
Apr 7 at 8:33 ]
const AuthNavigator = () => {
return(
<AuthStack.Navigator>
<Stack.Screen
name="Login"
component={Login}
options={{ headerShown: false }}
/>
<Stack.Screen
name="SignUp"
component={SignUp}
options={{ headerShown: false }}
/>
</AuthStack.Navigator>
);
}
const MyCustomNavigator = () => {
return(
<AppStack.Navigator>
<Stack.Screen
name="Home"
component={Home}
options={{ headerShown: false }}
/>
<Stack.Screen
name="ListScreen"
component={ListScreen}
options={{ headerShown: false }}
/>
<Stack.Screen
name="Settings"
component={Settings}
options={{ headerShown: false }}
/>
</AppStack.Navigator>
);
}
const AppNavigator = (props) => {
const isAuth = useSelector((state) => !!state.auth.access_token);
return (
<NavigationContainer>
{isAuth && <MyCustomNavigator />}
{!isAuth && <AuthNavigator />}
</NavigationContainer>
);
};
export default AppNavigator;
There isnt a switch navigator in react-navigation v5 or v6. however, i found switch very easy, and i find stack very difficult to use, so i continue to use react-navigation v4.2.0 or 4.4.1, so i can continue using switch