Mysterious random delay in loading content in React Native Expo app - reactjs

After the splash screen, and after the animated intro (see code below) , most of the time content appears immediately but sometimes randomly it takes up to 10-15s. After it does appear, any subsequent visit will work normal, for a while at least. I can't figure out if it's in-app bug or issue with server/database.
Here's a GIF illustrating the flow when it works properly:
Notice how list of Categories appear after "CHECK THIS OUT" Intro Image --- When the issue occurs, the Intro image fades-out, but the Categories don't appear for another 15s.
Using:
- Expo Go on iOS (only happens with expo publish, not in dev mode)
- Apollo and GraphQL to fetch content hosted on Heroku Postgres database.
- Heroku free dyno and Hobby-dev Postgres addon db (could this be the reason?)
If I minimize while this delay is ongoing, and come back to the app -- the Apollo useQuery hook will return an error.
if I don't minimize and just wait -- no error and it will eventually load.
It doesn't show the loading bar -- which should mean it's not stuck in "loading" state. Header and footer are visible - there's just empty screen where content should be.
HomeScreen.js:
import React, { useEffect, useState } from 'react'
import { View, Text, FlatList, Pressable, TouchableOpacity, ImageBackground, Image, Animated } from 'react-native'
import { useFocusEffect } from '#react-navigation/native';
import { gql, useQuery } from '#apollo/client'
import Bg from "../../../assets/background.png"
import Intro from "../../../assets/intro2x.png"
import { useFavContext } from '../../components/FavContext';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { StatusBar } from 'expo-status-bar';
import { LoadingLarge } from '../../components/Loading';
const HOME = gql`
query HomeData {
home {
data {
attributes {
...
}
}
}
}
`
const IntroMessageAnimation = (props) => {
const fadeAnimat = React.useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
useFocusEffect(
React.useCallback(() => {
setTimeout(() => {
Animated.timing(fadeAnimat, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
}, 500);
setTimeout(() => {
Animated.timing(fadeAnimat, {
toValue: 0,
duration: 500,
useNativeDriver: true,
}).start();
}, 2000);
},[]))
return (
<Animated.View // Special animatable View
key="1ab"
style={{
flex: 1,
opacity: fadeAnimat, // Bind opacity to animated value
justifyContent: 'center',
alignItems: 'center'
}}>
{props.children}
</Animated.View>
);
}
const IntroMessage = () => (
<IntroMessageAnimation >
<Image
style={{
width: 273,
height: 324,
}}
source={Intro}
defaultSource={Intro}
resizeMethod='scale'
resizeMode='contain'
/>
</IntroMessageAnimation >
)
const MainContentAnimation = (props) => {
const fadeMainAnimat = React.useRef(new Animated.Value(0)).current; // Initial value for opacity: 0
useEffect(() => {
const timeout = setInterval(() => {
Animated.timing(fadeMainAnimat, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
}, 500);
return () => clearInterval(timeout)
})
return (
<Animated.View // Special animatable View
style={{
flex: 1,
opacity: fadeMainAnimat, // Bind opacity to animated value
}}>
{props.children}
</Animated.View>
);
}
export default ({ navigation }) => {
const insets = useSafeAreaInsets();
const { favorites } = useFavContext()
const { data, loading, error } = useQuery(HOME, {
notifyOnNetworkStatusChange: true,
fetchPolicy: 'network-only', // Used for first execution
nextFetchPolicy: 'cache-first', // Used for subsequent executions
})
const [isReady, setIsReady] = useState(false)
useEffect(() => {
const timer = setTimeout(() => {
setIsReady(true);
}, 2700);
return () => clearTimeout(timer);
}, []);
if ( !isReady || loading ) {
return (
<ImageBackground source={Bg} resizeMode="cover" style={tw.style('flex-1 relative')}>
<StatusBar style='light'/>
<IntroMessage/>
</ImageBackground>
)
} else if ( loading ) {
return <LoadingLarge />
}
if (error) {
return <Text>Error :( </Text>;
}
const heading = data.home.data.attributes.Heading;
const subheading = data.home.data.attributes.Subheading;
const categories = data.home.data.attributes.categories
const MainScreen = () => (
<MainContentAnimation >
<View
style={{
// ... inset paddings ...
}}>
<StatusBar style='light'/>
<View>
<Text>{heading}</Text>
<Text>{subheading}</Text>
</View>
<View>
<FlatList
showsVerticalScrollIndicator={false}
data={categories.data}
renderItem={({ item }) => (
<CategoryItem
favorites={favorites}
navigation={navigation}
category={item}
onPress={() =>
navigation.navigate('CategoryScreen', { item: item })}
/>
)}
keyExtractor={(item) => item.id.toString()}
/>
</View>
</View>
</MainContentAnimation >
)
return (
<ImageBackground source={Bg} resizeMode="cover" style={tw.style('flex-1 relative')}>
<MainScreen/>
</ImageBackground>
)
}
If relevant, App.js:
// Initialize Apollo Client
const client = new ApolloClient({
uri: 'https://janazaapp.herokuapp.com/graphql/',
cache: new InMemoryCache({
typePolicies: {
Subsubcategory: {
merge: true
},
},
})
});
function HomeStackScreen() {
return (
<HomeStack.Navigator
initialRouteName="HomeScreen"
screenOptions={({ navigation }) => {
return {
detachPreviousScreen: !navigation.isFocused(),
headerShown: true,
headerMode: 'float',
}
}}
>
<HomeStack.Screen
name="HomeScreen"
component={FadeHomeScreen}
options={({ navigation }) => ({
headerTitle: ({props}) => <LogoTitle onPress={() => navigation.navigate('HomeScreen')} />,
})}
/>
// ... other screrens ...
</HomeStack.Navigator>
);
}
function MainScreen() {
const ref = React.useRef(null);
const newFavor = JSON.stringify(favor.current)
return (
<SafeAreaProvider>
<ApolloProvider client={client}>
<NavigationContainer theme={MyTheme} ref={ref}>
<FavProvider favor={newFavor}>
<Tab.Navigator
screenOptions={({ route, navigation }) => ({
// ... screen options ...
})}
>
<Tab.Screen
name="Home"
component={HomeStackScreen}
options={{ tabBarLabel: 'Home', headerShown: false }}
/>
<Tab.Screen
name="Favourites"
component={FavouritesStackScreen}
options={{ tabBarLabel: 'Favourites', headerShown: true }}
/>
<Tab.Screen
name="Faq"
component={FaqStackScreen}
options={{
tabBarLabel: 'FAQs',
headerShown: true,
}}
/>
<Tab.Screen
name="Search"
component={SearchStackScreen}
/>
</Tab.Navigator>
</FavProvider>
</NavigationContainer>
</ApolloProvider>
</SafeAreaProvider>
);
}
}

Related

React Native Stack Navigation not working

I am building a fitness app, and currently I am having a problem with React Native navigation stack, that I wasn't having before and not sure why this is happening.
As you can see in the video, after you select the type of training and number of days, it should go to the screen with the filtered workouts (WorkoutSelection).
Edit: I tried moving the navigation.navigate() outside the async function and it does navigate to the correct page, so I'm assuming it has something to do with it being inside a async function.
Here's the code for the confirm button:
async function confirmHandler() {
setIsLoading(true);
try {
const data = await getTrainingData(
selectedTrainingData.value,
selectedDaysData.value
);
setFilteredWorkouts(data);
setIsLoading(false);
navigation.navigate('WorkoutSelection');
} catch (error) {
console.log(error);
setIsLoading(false);
return (
<ErrorScreen message={'Something went wrong. Please try again.'} />
);
}
}
What is happening right now is whenever I click the confirm button, it goes to the home screen.
It does fetch the data from the API, I logged it to the console and it's working so, that shouldn't be the problem.
Here's the navigation code:
function TrainingOptionsStack() {
return (
<Stack.Navigator
screenOptions={{
headerStyle: { backgroundColor: GlobalStyles.colors.background },
headerTintColor: 'white',
headerTitleStyle: {
fontFamily: 'open-sans-semi-bold',
},
}}
>
<Stack.Screen
name='WorkoutsScreen'
component={WorkoutsScreen}
options={{ title: 'Workouts' }}
/>
<Stack.Screen
name='SelectPhase'
component={BlockOptions}
options={{ title: 'Training Phase' }}
/>
<Stack.Screen
name='WorkoutSelection'
component={WorkoutSelection}
options={{
title: 'Select a workout',
}}
/>
<Stack.Screen
name='SelectDay'
component={SelectDay}
options={{ title: 'Select Day' }}
/>
<Stack.Screen name='WorkoutOfTheDay' component={WorkoutOfTheDay} />
<Stack.Screen
name='PreviewModal'
component={PreviewModal}
options={{ presentation: 'modal', title: false }}
/>
</Stack.Navigator>
);
}
function AppNavigation() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={{
headerStyle: { backgroundColor: GlobalStyles.colors.background },
headerTintColor: 'white',
tabBarStyle: {
backgroundColor: GlobalStyles.colors.primary700,
paddingTop: 5,
height: 90,
},
headerTitleStyle: {
fontFamily: 'open-sans-semi-bold',
},
tabBarActiveTintColor: 'white',
}}
>
<Tab.Screen
name='Home'
component={HomeScreen}
options={{
title: 'Home',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='home-outline'
size={34}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
<Tab.Screen
name='Workouts'
component={TrainingOptionsStack}
options={{
title: 'Workouts',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='barbell-outline'
size={34}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
headerShown: false,
}}
/>
<Tab.Screen
name='RepMaxCalculator'
component={RepMaxCalculator}
options={{
title: 'Max Rep Calculator',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='calculator-outline'
size={30}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
<Tab.Screen
name='ProgressChart'
component={ProgressChart}
options={{
title: 'Progress Chart',
tabBarIcon: ({ focused }) => {
return (
<Ionicons
name='bar-chart-outline'
size={30}
color={
focused
? GlobalStyles.colors.primary400
: GlobalStyles.colors.primary500
}
/>
);
},
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
And here's the 'WorkoutSelection' component -
import React, { useEffect } from 'react';
import { FlatList, StyleSheet, View } from 'react-native';
import SelectWorkout from '../components/SelectWorkout';
import FlatButton from '../components/UI/buttons/FlatButton';
import { GlobalStyles } from '../constants/styles';
import useAppContext from '../store/AppContext';
import { doc, setDoc, serverTimestamp } from 'firebase/firestore';
import { db } from '../firebase/firebaseConfig';
function WorkoutSelection({ navigation }) {
const {
filteredWorkouts,
previewWorkoutHandler,
setWorkoutPreviewTitle,
setCurrentWorkout,
currentWorkout,
userIsAuthenticated,
} = useAppContext();
// useEffect(() => {
// const docRef = doc(db, 'users', userIsAuthenticated.uid);
// async function addCurrentWorkoutToDataBase() {
// await setDoc(docRef, 'CurrentWorkout', currentWorkout, { merge: true });
// }
// addCurrentWorkoutToDataBase();
// }, [currentWorkout]);
useEffect(() => {
navigation.setOptions({
headerRight: () => (
<FlatButton
style={styles.headerButton}
onPress={() => navigation.replace('WorkoutsScreen')}
>
Cancel
</FlatButton>
),
});
}, []);
const id = filteredWorkouts
.flatMap((item) => item.workouts)
.flatMap((item) => item.id);
function previewHandler(item) {
previewWorkoutHandler(id[0]);
setWorkoutPreviewTitle(item.title);
navigation.navigate('PreviewModal');
}
function selectWorkoutHandler(item) {
setCurrentWorkout([item]);
navigation.navigate('WorkoutsScreen');
}
return (
<View style={styles.rootContainer}>
<FlatList
data={filteredWorkouts}
renderItem={({ item }) => (
<SelectWorkout
name={item.title}
onSelect={() => selectWorkoutHandler(item)}
onShowPreview={() => previewHandler(item)}
/>
)}
keyExtractor={(item) => item.id}
/>
</View>
);
}
export default WorkoutSelection;
const styles = StyleSheet.create({
rootContainer: {
backgroundColor: GlobalStyles.colors.background,
flex: 1,
},
headerButton: {
textDecorationLine: 'none',
marginRight: 24,
fontSize: 16,
fontFamily: 'open-sans-semi-bold',
},
});
Here's a little video to demonstrate the problem -
https://drive.google.com/file/d/1OoKlj2tZsL6sYqpcUnKUcQhAjmhTZGJZ/view?usp=share_link
What I expect is to go to the 'WorkoutSelection" screen so the user can complete the workout selection flow and I can update it in Firebase.
Appreciate any help!
Have you import all the component on above page?
e.g. if you want to use WorkoutSelection component then import that.
like this.
import {WorkoutSelection} from 'path here'.
and then use that component for the navigation.
Hope you got my point.
It is happening because you are using replace function instead of goBack or navigate in WorkoutSelection
So first change from replace to goBack or navigate
after selecting values WorkoutSelection , if you want to refresh WorkoutsScreen so your selected filter can be apply then you have two ways to do that
call api on focus change in WorkoutsScreen (follow below mentioned link )
https://reactnavigation.org/docs/function-after-focusing-screen/
fire event from WorkoutSelection before calling goBack or navigate and that event should be listened on WorkoutsScreen so when you get event , you can call api and refresh screen as per filter selection
I figured out the problem. I had to go back a few commits and start adding one thing at a time to try to get to the point where I was having the same problem.
The problem was in the App.js, the rootFunction that I created wasn't returning anything conditionally as I wanted. I had to restructure it a bit and got it working.
Totally missed it. Thanks all for the help!

React native - Transform with key of TranslateX must be a number

I have a little problem with my bottom tabs nav. I try to follow this tutorial to customize my navbar : https://www.youtube.com/watch?v=t0GMzGgHwgk
The github of his video : https://github.com/ReactiveKoding/customtabbar/blob/main/App.js
But, when I try to test it I have an error : "Transform with key of 'translateX' must be a number", but he doesn't have it...
This is my code, if you have an issue to it I will really appreciate it !
import { ScrollView, StyleSheet, Text, View, TextInput, Image, Button, TouchableOpacity, Animated, Dimensions } from 'react-native';
import 'react-native-gesture-handler';
import { LinearGradient } from 'expo-linear-gradient';
import { useFonts } from 'expo-font';
import { Linking } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import Icon from 'react-native-vector-icons/FontAwesome';
import { useRef } from 'react';
const Tab = createBottomTabNavigator();
function User({ navigation }) {
return (
<View><Text>Construction</Text></View>
)
}
function Shower({ navigation }) {
return (
<View><Text>Construction</Text></View>
)
}
function Search({ navigation }) {
return (
<View><Text>Construction</Text></View>
)
}
function Home({ navigation }) {
return (
<View><Text>Construction</Text></View>
)
}
export default function MyTabs() {
const tabOffsetValue = useRef(new Animated.Value(0)).current;
return (
<NavigationContainer screenOptions={{ headerShown: false }}>
<Tab.Navigator screenOptions={({ route }) => ({
tabBarIcon: ({focused, color, size}) => {
let iconName;
if (route.name === 'Home') {
iconName = 'home';
} else if (route.name === 'Search') {
iconName = 'search';
} else if (route.name === 'Shower') {
iconName = 'shower';
} else if (route.name === 'User') {
iconName = 'user';
}
return <Icon name={iconName} size={size} color={color} />
},
tabBarActiveTintColor: '#2A27B0',
tabBarInactiveTintColor: '#CECECE',
tabBarShowLabel: false,
tabBarStyle: {
backgroundColor: '#fff',
height: 60
}
})}>
<Tab.Screen options={{headerShown: false}} name="Home" component={Home} listeners={({navigation, route}) => ({
tabPress: e => {
Animated.spring(tabOffsetValue, {
toValue: 0,
useNativeDriver: true
}).start();
}
})}/>
<Tab.Screen options={{headerShown: false}} name="Search" component={Search} listeners={({navigation, route}) => ({
tabPress: e => {
Animated.spring(tabOffsetValue, {
toValue: 90,
useNativeDriver: true
}).start();
}
})}/>
<Tab.Screen options={{headerShown: false}} name="Shower" component={Shower} listeners={({navigation, route}) => ({
tabPress: e => {
Animated.spring(tabOffsetValue, {
toValue: 175,
useNativeDriver: true
}).start();
}
})}/>
<Tab.Screen options={{headerShown: false}} name="User" component={User} listeners={({navigation, route}) => ({
tabPress: e => {
Animated.spring(tabOffsetValue, {
toValue: 268,
useNativeDriver: true
}).start();
}
})}/>
</Tab.Navigator>
<View style={{
width: 60,
height: 2,
backgroundColor: '#2A27B0',
position: 'absolute',
bottom: 58,
left: 15,
borderRadius: 20,
transform: [
{ translateX: tabOffsetValue }
]
}}></View>
</NavigationContainer>
)
}
I already reed this issue : How to fix "transform with key of translatex must be a number" error in React Native?
But I don't understand if my problem is the same and how I have to implement the Animated.View
Thanks by advance !

React native Navigation says "NAVIGATE" {"name":"Car"} Was not handle by any navigator?

I am trying to develop an app where there are two scenarios of Navigation one is before longing navigation and another is after login navigation. So And navigation {"name": "Car"} screen should only display once the user login, but when I tried to do that it says `"NAVIGATE" {"name": "Car"} Was not handled by any navigation. Please guide me on how can I solve it, Here is my code example. and on MainStackScreen there is also link to Bottom navigation.
import * as React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import BottomNavigation from './BottomNavigation';
import Car from '../src/Screen/Car/Car';
import Login from '../src/Screen/Auth/Login/Login';
import ForgetPassword from '../src/Screen/Auth/Forget/ForgetPassword';
import OfflineNotice from '../src/Screen/Offline/Offline';
import Screen from '../src/Screen/Offline/Screen';
import AsyncStorage from '#react-native-async-storage/async-storage';
function Navigation1({navigation}) {
const [age, setAge] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const readData = async () => {
try {
const userAge = await AsyncStorage.getItem('#storage_Key');
setLoading(false);
setAge(userAge);
} catch (e) {
console.log('Error While Fetching Storage data ');
}
};
React.useEffect(() => {
const interval = setInterval(() => {
readData();
}, 100);
return () => {
clearTimeout(interval);
};
}, [navigation]);
const getLageoginPage = async () => {
if (await AsyncStorage.getItem('#storage_Key')) {
} else {
navigation.navigate('Login');
}
};
React.useEffect(() => {
getLageoginPage();
}, [navigation]);
const AuthStack = createStackNavigator();
const AuthStackScreen = () => (
<AuthStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: 'white',
height: 47,
},
}}>
<Stack.Screen
name="Login"
component={Login}
options={{title: 'Dashboard', headerShown: false}}
/>
<Stack.Screen
name="Forget"
component={ForgetPassword}
options={{title: 'Dashboard', headerShown: false}}
/>
</AuthStack.Navigator>
);
const MainStack = createStackNavigator();
const MainStackScreen = () => (
<MainStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: 'white',
height: 45,
},
}}>
<MainStack.Screen
name="Home"
component={BottomNavigation}
options={{title: 'Main', headerShown: false}}
/>
<MainStack.Screen
name="Car"
component={Car}
options={{title: 'Car', headerShown: false}}
/>
</MainStack.Navigator>
);
const Stack = createStackNavigator();
let network = OfflineNotice();
if (network === false) {
return <Screen />;
}
return (
<NavigationContainer>
<Stack.Navigator>
{age !== null && !loading ? (
<Stack.Screen
name="Main"
component={MainStackScreen}
options={{title: 'Dashboard', headerShown: false}}
/>
) : (
<Stack.Screen
name="Auth"
component={AuthStackScreen}
options={{title: 'Dashboard', headerShown: false}}
/>
)}
</Stack.Navigator>
</NavigationContainer>
);
}
export default Navigation1;
And here it is the Bottom Navigation code.
import React from 'react';
import {createBottomTabNavigator} from '#react-navigation/bottom-tabs';
import {Dimensions} from 'react-native';
import Start from '../src/Screen/Start/Start';
import Driver from '../src/Screen/Driver/Driver';
import Ionicons from 'react-native-vector-icons/Ionicons';
import MaterialCommunityIcons from 'react-native-vector-icons/MaterialCommunityIcons';
const Tab = createBottomTabNavigator();
const DEVICE_WIDTH = Dimensions.get('window').width;
const BottomNavigation = () => {
return (
<Tab.Navigator
initialRouteName={'Car'}
lazy={true}
tabBarOptions={{
showLabel: false,
inactiveTintColor: '#00A870',
backgroundColor: 'white',
activeTintColor: '#00A870',
labelStyle: {
fontSize: 12,
},
style: {
backgroundColor: 'white',
position: 'absolute',
marginLeft: 120, // Use margins as you required
marginRight: 100,
marginBottom: 20,
width: DEVICE_WIDTH - 230, // Or using a percentage as required
borderRadius: 25,
},
}}>
<Tab.Screen
name="Start"
component={Start}
options={{
tabBarLabel: 'Start',
tabBarIcon: ({focused}) => (
<MaterialCommunityIcons
name={focused ? 'steering' : 'steering-off'}
color={focused ? '#52CA00' : '#231F20'}
size={25}
/>
),
}}
listeners={({navigation, route}) => ({
tabPress: e => {
e.preventDefault();
navigation.navigate('Start');
},
})}
/>
<Tab.Screen
name="Driver"
component={Driver}
listeners={({navigation, route}) => ({
tabPress: e => {
e.preventDefault();
navigation.navigate('Driver');
},
})}
options={{
tabBarLabel: 'Driver',
tabBarIcon: ({focused}) => (
<Ionicons
name={focused ? 'map-outline' : 'map'}
color={focused ? '#52CA00' : '#231F20'}
size={25}
/>
),
}}
/>
</Tab.Navigator>
);
};
export default BottomNavigation;
Used Place On successful login.
.then(async response2 => {
setLoading(false);
// Check if user has email or not
if (response2.data.email) {
try {
await AsyncStorage.setItem('#storage_Key', data.token);
await AsyncStorage.setItem(
'#storage_Key_refresh',
data.refresh_token,
);
} catch (error) {
console.log('AsyncStorage Error: ' + error.message);
}
try {
navigation.navigate('Car');
} catch (error) {
console.log('Navigation Error: ' + error.message);
}
} else {
setLoading(false);
Alert.alert(
'Login in successfull',
'Sorry, you do not have access rights to Digital fleet',
);
}
})

Why my Bottom Tabs Navigator is re rendering unnecessary?

I am making an app in which there are three bottom tabs screen. HomeScreen, CartScreen and Profile Screen. I was wanting to check the re renders of my app using "Why did you re render" npm package and found a critical bug or is it so.
The MainTabScreen(The screen in which all the bottom tabs are nested) is re rendering as many times I change the screen, just because its props changed.
Here is the console log :
Here is the code of the MainTabScreen.js:
const MainTabScreen = () => {
return (
<Tab.Navigator
tabBarOptions={{
showLabel: false,
style: {
position: "absolute",
height: 40,
bottom: 0,
right: 0,
left: 0,
backgroundColor: "#fff",
},
}}
>
<Tab.Screen
name="MainHome"
component={MainHomeScreen}
options={{
tabBarIcon: ({ focused }) => (
<Icon.Home
// color={"#f62459"}
color={focused ? "#f62459" : "#302f2f"}
height={28}
width={28}
/>
),
}}
/>
<Tab.Screen
name="MainCart"
component={MainCartScreen}
options={{
tabBarIcon: ({ focused }) => (
<Icon.ShoppingCart
// color="#302f2f"
color={focused ? "#f62459" : "#302f2f"}
width={28}
height={28}
/>
),
}}
/>
<Tab.Screen
name="MainProfile"
component={MainProfileScreen}
options={{
tabBarIcon: ({ focused }) => (
<Icon.User
// color="#302f2f"
color={focused ? "#f62459" : "#302f2f"}
height={28}
width={28}
/>
),
}}
/>
</Tab.Navigator>
);
};
export default MainTabScreen;
Edit 11:27 05-09-21
The MainTabScreen renders inside AppStack screen which renders inside the AuthStack.js for authentication and this AuthStack.js renders inside the App.js
Here is the AppStack.js code:
const AppStack = () => {
return(
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="MainTabScreen" component={MainTabScreen} />
<Stack.Screen name="Product" component={Product} />
<Stack.Screen name="OrderScreen" component={OrderScreen} />
</Stack.Navigator>
)
This is the AuthStack.js
import React, {
useState,
useEffect,
useMemo,
useReducer,
useContext,
} from "react";
import { View, Text, Platform } from "react-native";
import AppStack from "./navigation/AppStack";
import RootStack from "./navigation/RootStack";
import { AuthContext } from "./context";
import * as SecureStore from "expo-secure-store";
import LottieView from "lottie-react-native";
import { useIsMounted } from "../screens/useIsMounted";
const AuthStack = () => {
const initialLoginState = {
isLoading: true,
accessToken: null,
};
// The rest of the authentication logic...
return (
<AuthContext.Provider value={authContext}>
{loginState.accessToken !== null ? <AppStack /> : <RootStack />}
</AuthContext.Provider>
);
};
export default AuthStack;
App.js
import React from "react";
import { StyleSheet, Text, View, Dimensions } from "react-native";
import { NavigationContainer } from "#react-navigation/native";
import store from "./redux/store";
const App = () => {
return (
<Provider store={store}>
<NavigationContainer>
<AuthStack />
</NavigationContainer>
</Provider>
);
};
export default App;
i found the solution...
use:
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
const Tab = createBottomTabNavigator();
`<Tab.Navigator
tabBar={props => <MyTabBar {...props} />}>`
create separately TabBar (for example):
function MyTabBar({ state, descriptors, navigation, position }) {
const edge = useSafeAreaInsets();
const { colors } = useTheme();
return (
<View style={{ flexDirection: 'row', height: edge.bottom + 70, alignItems: 'center', justifyContent: 'space-between', backgroundColor: colors.cell }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const label =
options.tabBarLabel !== undefined
? options.tabBarLabel
: options.title !== undefined
? options.title
: route.name;
const isFocused = state.index === index;
const onPress = () => {
const event = navigation.emit({
type: 'tabPress',
target: route.key,
canPreventDefault: true,
});
if (!isFocused && !event.defaultPrevented) {
// The `merge: true` option makes sure that the params inside the tab screen are preserved
navigation.navigate({ name: route.name, merge: true });
}
};
if (label === 'Browse Radios') {
return <CenterTabBar isFocused={isFocused} onPress={() => { onPress(); }} />
} else {
return <MyTabBarButton isFocused={isFocused} onPress={() => { onPress(); }} label={label} />
}
})}
</View>
);
}
and create separately buttons for TabBar (for example):
const CenterTabBar = ({ onPress, isFocused }) => {
return (
<TouchableOpacity style={{
alignItems: 'center',
flex: 1
}}
onPress={() => {
onPress();
}}
>
{!isFocused && <Image style={styles.centerTabBar} source={require('../../assets/images/tab_browse.png')} />}
{isFocused && <Image style={styles.centerTabBar} source={require('../../assets/images/tab_browse_.png')} />}
</TouchableOpacity>
);
}
const MyTabBarButton = ({ onPress, isFocused, label }) => {
const { colors, dark } = useTheme();
return (
<TouchableOpacity style={{
alignItems: 'center',
flex: 1
}}
onPress={() => { onPress() }}
>
<Animated.Image style={[styles.icon_tab, { tintColor: isFocused ? '#00ACEC' : colors.tint }]} source={GetIconTab(label)} />
{isFocused && <Animated.View style={styles.circle} entering={BounceIn} />}
</TouchableOpacity>
);
}

React Native, Redirect from Splash Screen to another screen

I am trying to navigate from SplashScreen to another screen after 5 second and I get this error:
Code from SplashScreen:
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View,Alert, ImageBackground,Image,AsyncStorage,ActivityIndicator} from 'react-native';
import { StackNavigator } from 'react-navigation';
export default class App extends Component<Props> {
componentDidMount () {
setTimeout(() => {
// this._retrieveData();
this.props.navigation.replace("Journeys");
}, 5000)
}
render() {
return (
<ImageBackground
source={require('../../assets/rsz_blue_bg.png')}
style = {{flex: 1, height: '100%', width: '100%', resizeMode : 'stretch'}}>
<Image
source={require('../../assets/logo_white.png')}
style = {{ width: '100%', height:'70%', marginTop: 100 }}
/>
</ImageBackground>
);
}
}
Code from App.js:
import React, { useContext, useEffect } from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createDrawerNavigator } from '#react-navigation/drawer';
import Icon from 'react-native-vector-icons/Ionicons';
//I am doing the imports here but I am cutting down the code to be able to post :)
const AuthStack = createStackNavigator()
const AuthStackScreen = () => (
<AuthStack.Navigator screenOptions={{headerShown: false}}>
<AuthStack.Screen name="SignIn" component={SignInScreen} />
<AuthStack.Screen name="SignUp" component={SignUpScreen} />
</AuthStack.Navigator>
)
const FirstStack = createStackNavigator()
const FirstStackScreen = ({navigation}) => (
<FirstStack.Navigator screenOptions={{
headerStyle: {
backgroundColor: '#009387'
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold',
// alignSelf: 'center',
}
}}>
<FirstStack.Screen
name="Journeys"
component={TrackCreateScreen}
options={{
headerLeft: () => (
<Icon.Button
name="ios-menu"
size={25}
backgroundColor="#009387"
onPress={() => {navigation.openDrawer()}}></Icon.Button>
)
}}
/>
</FirstStack.Navigator>
)
const TrackListStack = createStackNavigator()
const TrackListScreens = ({navigation}) => (
<TrackListStack.Navigator screenOptions={{
headerStyle: {
backgroundColor: '#009387'
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold'
}
}}>
<TrackListStack.Screen
name="History"
component={TrackListScreen}
options={{
headerLeft: () => (
<Icon.Button
name="ios-menu"
size={25}
backgroundColor="#009387"
onPress={() => {navigation.openDrawer()}}></Icon.Button>
)
}}
/>
<TrackListStack.Screen name="Journey Details" component={TrackDetailScreen} />
</TrackListStack.Navigator>
)
const AccountStack = createStackNavigator()
const AccountScreens = ({navigation}) => (
<AccountStack.Navigator screenOptions={{
headerStyle: {
backgroundColor: '#009387'
},
headerTintColor: '#fff',
headerTitleStyle: {
fontWeight: 'bold'
}
}}>
<AccountStack.Screen
name="Account"
component={AccountScreen}
options={{
headerLeft: () => (
<Icon.Button
name="ios-menu"
size={25}
backgroundColor="#009387"
onPress={() => {navigation.openDrawer()}}></Icon.Button>
)
}}
/>
</AccountStack.Navigator>
)
const AppStack = createDrawerNavigator()
const AppStackScreen = ({navigation}) => (
<AppStack.Navigator drawerContent={props => <DrawerContent {...props}/>}>
<AppStack.Screen name="Journey" component={FirstStackScreen} />
<AppStack.Screen name="History" component={TrackListScreens} />
<AppStack.Screen name="Account" component={AccountScreens} />
</AppStack.Navigator>
)
const App = () => {
const { state } = useContext(AuthContext)
if(state.isLoading) {
return <LoadingScreen />
}
return (
<NavigationContainer>
<SplashScreen/>
</NavigationContainer>
)
}
export default () => (
<AuthProvider>
<LocationProvider>
<TrackProvider>
<App />
</TrackProvider>
</LocationProvider>
</AuthProvider>
)
Or if you have a better option to do this redirection it's ok anyway.
I tried various methods and for sure I am doing something wrong.
Please help.
this is not accessible in setTimeout.
componentDidMount () {
let self = this;
setTimeout(() => {
// this._retrieveData();
self.props.navigation.replace("Journeys");
}, 5000)
}

Resources