Why my Bottom Tabs Navigator is re rendering unnecessary? - reactjs

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>
);
}

Related

Payload not handled by any navigator in react navigation 6

I am updating my react-native app to use react navigation 6 and I'm trying to figure out how to connect to a screen.
The screen I'm trying to link/navigate to uses a bottomtabnavigator, and looks like this:
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
export const PatientScreen = () => {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator screenOptions={{ headerShown: false }} initialRouteName={'Visits'}>
<Tab.Screen name="Visits" component={VisitsTab}
options={{
tabBarLabel: 'Visits',
tabBarIcon: ({ color, size }) => (
<FontAwesome name="street-view" color={color} size={size} />
),
}}
/>
<Tab.Screen name="Chart" component={ChartTab}
options={{
tabBarLabel: 'Charts',
tabBarIcon: ({ color, size }) => (
<FontAwesome name="id-badge" color={color} size={size} />
),
}}
/>
<Tab.Screen name="Goals" component={GoalsTab}
options={{
tabBarLabel: 'Edit Goals',
tabBarIcon: ({ color, size }) => (
<FontAwesome name="trophy" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
);
}
I am trying to link to the above from within a screen that is part of a different navigator. That screen looks like this:
import { StyleSheet, Text, View, Tab, Button } from 'react-native';
import { PatientScreen } from './PatientScreen';
export const CaseloadScreen = ({navigation}) => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Caseload</Text>
<Button title="Patient"
onPress={() => navigation.navigate('PatientScreen', { screen: 'Visits' })}
/>
</View>
);
}
When I click on the button above, I get this error:
The action 'NAVIGATE' with payload undefined was not handled by any navigator.
Do you have a screen named 'PatientScreen'?
So clearly I'm missing something, but it's unclear to me what that is. How do I make this PatientScreen which uses a bottomTabNavigator, linkable from the button link I listed above?
My navigator for the Caseload screen looks like this:
import React from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import {
createDrawerNavigator,
DrawerContentScrollView,
DrawerItemList,
DrawerItem,
} from '#react-navigation/drawer';
import { LoginScreen } from '../screens/LoginScreen';
import { CaseloadScreen } from '../screens/CaseloadScreen';
import { WeeklyScreen } from '../screens/WeeklyScreen';
import { ProfileScreen } from '../screens/ProfileScreen';
import { Ionicons, FontAwesome, MaterialCommunityIcons, AntDesign } from '#expo/vector-icons';
import { AvailablePatientScreen } from '../screens/AvailablePatientScreen';
const CustomDrawerContent = (props) => {
return (
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} style={{
activeTintColor: {
color: '#fff',
},
}} />
</DrawerContentScrollView>
);
}
const Drawer = createDrawerNavigator();
const DrawerNavigation = () => {
const icons = {
tintColor: '#fff',
size: 20,
}
return (
<Drawer.Navigator initialRouteName={'Caseload'}n
drawerContent={(props) => <CustomDrawerContent {...props} />}
screenOptions={{
labelStyle: {
color: '#fff',
},
inactiveTintColor: {
color: '#fff',
},
drawerStyle: {
backgroundColor: 'rgb(61,77,138)',
width: 240,
activeTintColor: 'rgb(61,77,138)',
activeBackgroundColor: '#fff',
inactiveTintColor: '#fff',
inactiveBackgroundColor: 'rgb(61,77,138)',
},
}}
>
<Drawer.Screen name="Caseload" component={CaseloadScreen} options={{
drawerIcon: () => (<Ionicons name="ios-home" size={icons.size} color={icons.tintColor} />)
}} />
<Drawer.Screen name="Weekly Summary" component={WeeklyScreen} options={{
drawerIcon: () => (<FontAwesome name="bar-chart" size={icons.size} color={icons.tintColor} />)
}} />
<Drawer.Screen name="Available RiverKids" component={AvailablePatientScreen} options={{
drawerIcon: () => (<FontAwesome name="child" size={icons.size} color={icons.tintColor} />)
}} />
<Drawer.Screen name="My Profile" component={ProfileScreen} options={{
drawerIcon: () => (<AntDesign name="profile" size={icons.size} color={icons.tintColor} />)
}} />
{/* <Drawer.Screen name="Patient" component={PatientScreen} options={{
drawerIcon: () => (<FontAwesome name="bar-chart" size={icons.size} color={icons.tintColor} />)
}} /> */}
<Drawer.Screen name="Login" component={LoginScreen} options={{ title: 'Sign Out',
drawerIcon: () => (<MaterialCommunityIcons name="logout" size={icons.size} color={icons.tintColor} />)
}} />
</Drawer.Navigator>
);
}
export const Navigator = () => {
return (
<NavigationContainer>
<DrawerNavigation />
</NavigationContainer>
);
}
In your drawer navigator there is no screen defined with the name 'PatientScreen';
I can see a commented screen in your drawer navigator which has a name Patient and the component is PatientScreen. If you wish to navigate the Patient Screen from the drawer, do uncomment that code and then do the below
import { StyleSheet, Text, View, Tab, Button } from 'react-native';
export const CaseloadScreen = ({navigation}) => {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Caseload</Text>
<Button title="Patient"
onPress={() => navigation.navigate('Patient', { screen: 'Visits' })}
/>
</View>
);
}
Or else if you wish to navigate the PatientScreen only from the Caseload Screen, then create a Stack Navigator with the caseload and patient screen and then in the drawer navigator use that stack navigator in place of caseload screen

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',
);
}
})

Mysterious random delay in loading content in React Native Expo app

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>
);
}
}

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)
}

React Native: Creating and passing a StackNavigator to a Bottom Tab Navigator

I was trying to use my new created navigator stack on my bottom navigator tabs item so that I can use it instead of a regular screen.
So far I created the new stack navigator:
const FavoritesNav = () => {
return(
<FavoritesStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: Colors.primaryColor,
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 17
}
}}>
<FavoritesStack.screen
name="Favorite"
component={FavoritesScreen}
/>
<FavoritesStack.screen
name="MealDetail"
component={MealDetailScreen}
/>
</FavoritesStack.Navigator>
);
};
And then, I tried to pass this on one of the item of my bottom navigator:
<MealsFavTabNavigator.Screen
name="Favorites"
component={FavoritesNav}
options={{
tabBarIcon: ({ focused, color, size }) => (
<Ionicons name="ios-star" size={25} color={focused ? "tomato" : "black"} />
)
}}
/>
This throws me an error: A navigator can only contain 'Screen' components as its diection children (found [object, object])
How can I pass my newly created stack to the bottom tab nav?
PS. Here's my complete code:
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { Ionicons } from '#expo/vector-icons';
import { Platform } from 'react-native';
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs';
import CategoriesScreen from '../screens/CategoriesScreen';
import CategoryMealsScreen from '../screens/CategoryMealsScreen';
import MealDetailScreen from '../screens/MealDetailScreen';
import FavoritesScreen from '../screens/FavoritesScreen';
import HeaderButton from '../components/HeaderButton';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { CATEGORIES } from '../data/dummy-data';
import Colors from '../constants/colors';
const MealsNav = createStackNavigator();
const MealsNavigator = () => {
return (
<MealsNav.Navigator
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: Colors.primaryColor,
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 17
}
}}
>
<MealsNav.Screen
name="Categories"
component={CategoriesScreen}
options={{
title: 'Meals Categories'
}}
/>
<MealsNav.Screen
name="CategoryMeals"
component={CategoryMealsScreen}
options={({ route }) => {
const catId = route.params.categoryId;
const selectedCategory = CATEGORIES.find((cat) => cat.id === catId);
return {
title: selectedCategory.title,
};
}}
/>
<MealsNav.Screen
name="MealDetail"
component={MealDetailScreen}
options={{
title: 'Meal Detail',
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Favorite'
iconName='ios-star'
onPress={() => console.log('Mark as the favorite')}
/>
</HeaderButtons>
),
}}
/>
</MealsNav.Navigator>
);
};
const MealsFavTabNavigator =
Platform.OS === 'android'
? createMaterialBottomTabNavigator()
: createBottomTabNavigator();
const getNavigationOptions = () => {
if (Platform.OS === 'ios') {
// Props for the ios navigator
return {
labeled: false,
initialRouteName: 'Meals',
tabBarOptions: {
activeTintColor: 'tomato',
inactiveTintColor: 'black',
},
};
}
// Props for android
return {
initialRouteName: 'Favorites',
activeColor: 'tomato',
inactiveColor: 'black',
barStyle: { backgroundColor: Colors.primaryColor },
shifting: true
};
};
const MealsTabNav = () => {
return (
<NavigationContainer>
<MealsFavTabNavigator.Navigator {...getNavigationOptions()} >
<MealsFavTabNavigator.Screen
name="Meals"
component={MealsNavigator}
options={{
tabBarIcon: ({ focused, color, size }) => (
<Ionicons name="ios-restaurant" size={25} color={focused ? "tomato" : "black"} />
)
}}
/>
<MealsFavTabNavigator.Screen
name="Favorites"
component={FavoritesNav}
options={{
tabBarIcon: ({ focused, color, size }) => (
<Ionicons name="ios-star" size={25} color={focused ? "tomato" : "black"} />
)
}}
/>
</MealsFavTabNavigator.Navigator>
</NavigationContainer>
);
};
const FavoritesStack = createStackNavigator();
const FavoritesNav = () => {
return(
<FavoritesStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: Colors.primaryColor,
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 17
}
}}>
<FavoritesStack.screen
name="Favorite"
component={FavoritesScreen}
/>
<FavoritesStack.screen
name="MealDetail"
component={MealDetailScreen}
/>
</FavoritesStack.Navigator>
);
};
export default MealsTabNav;
Thanks for those who will help.
Guess you are importing FavoritesScreen but using FavoriteScreen in code.
import FavoritesScreen from '../screens/FavoritesScreen';
const FavoritesNav = () => {
<NavigationContainer>
<FavoritesStack.Navigator
screenOptions={{
headerStyle: {
backgroundColor: Colors.primaryColor,
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 17
}
}}>
<FavoritesStack.screen
name="Favorite"
component={FavoritesScreen}
/>
<FavoritesStack.screen
name="MealDetail"
component={MealDetailScreen}
/>
</FavoritesStack.Navigator>
</NavigationContainer>
};

Resources