React Native Stack Navigation not working - reactjs

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!

Related

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

navigating from a tab screen to a stack screen

For an IOS application, I have a stack that gets called in my tab navigator. I am trying to navigate from a bottom tab screen to a screen in the stack but I am getting the following error.
undefined is not an object (evaluating
'_this.props.navigation.navigate')
I would like to render the bottom tab across all screens. I am noticing this causes some interesting issues with goBack() as well in other places.
How can I navigate from the bottom tab screen to a stack screen?
Is the current implementation a bad practice?
I have provided this demo as well as the following code below. I think it is related to prop passing.
const Tab = createBottomTabNavigator();
function MyTabs() {
return (
<Stack.Navigator
initialRouteName="Home">
<Stack.Screen name="Home" component= {Home} options={{headerShown: false}}/>
<Stack.Screen name="Screen1" component= {Screen1} options={{headerShown: false}}/>
</Stack.Navigator>
);
);
}
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
initialRouteName="Home"
screenOptions={{
tabBarActiveTintColor: '#F60081',
tabBarInactiveTintColor: '#4d4d4d',
tabBarStyle: {
backgroundColor: '#d1cfcf',
borderTopColor: 'transparent',
},
}}
>
<Tab.Screen
name="Home"
component={MyTabs}
options={{
tabBarLabel: 'Home',
headerShown: false,
tabBarIcon: ({ color, size }) => (
<MaterialCommunityIcons name="home" color={color} size={size} />
),
}}
/>
</Tab.Navigator>
</NavigationContainer>
);
}
const Stack = createStackNavigator();
import * as React from 'react';
import {
Text,
View,
StyleSheet,
TouchableOpacity,
ImageBackground,
} from 'react-native';
const Images = [
{ id: '1', uri: require('./assets/snack-icon.png'), text: 'Test' },
{ id: '2', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
{ id: '3', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
{ id: '4', uri: require('./assets/snack-icon.png') /*text: "Test"*/ },
];
export default class Home extends React.Component {
thisNavigate = () => {
this.props.navigation();
};
renderList = (props) => {
return Images.map((item, i) => {
return (
<TouchableOpacity
onPress={() => this.props.thisNavigate.navigate('Screen2')}>
<ImageBackground
source={item.uri}
style={{
width: '100%',
height: 100,
shadowColor: '#000',
shadowOffset: { width: 1, height: 4 },
shadowOpacity: 1,
}}
imageStyle={{ borderRadius: 10 }}></ImageBackground>
</TouchableOpacity>
);
});
};
render() {
return <View style={{ flex: 1 }}>{this.renderList()}</View>;
}
}
In your example your Home component is:
export default class Home extends React.Component {
render() {
return <Screen1/> // here navigation is not passed because it is rendered not by navigator but manually
}
}
Just remove it, remove its Stack.Screen from MyTabs and set initialRouteName to Screen1, and it will work.
EDIT:
If you want to keep Home as it is, pass navigation like that:
export default class Home extends React.Component {
render() {
return <Screen1 navigation={this.props.navigation}/>
}
}
Or use useNavigation hook instead of props in Screen1 to get navigation.
EDIT2:
Working demo.
I think that you need to wrap your component withNavigation HOC
https://reactnavigation.org/docs/4.x/with-navigation/
That's because your component not directly from the component Navigator, so they don't have this.props.navigation, to make your component have navigation props in Class Component, you need to wrap your component using withNavigation HOC
example:
// Screen1.js
class Home extends React.Component {
renderList = (props) => {
return Images.map((item, i) => {
return (
<TouchableOpacity
onPress={() => this.props.navigation.navigate('Screen2')}>
<ImageBackground
source={item.uri}
style={{
width: '100%',
height: 100,
shadowColor: '#000',
shadowOffset: { width: 1, height: 4 },
shadowOpacity: 1,
}}
imageStyle={{ borderRadius: 10 }}></ImageBackground>
</TouchableOpacity>
);
});
};
render() {
return <View style={{ flex: 1 }}>{this.renderList()}</View>;
}
}
export default withNavigation(Home);
sorry, but I think withNavigation is removed from v5 / v6
but you can make one like this
import { useNavigation } from '#react-navigation/native';
// withNavigation.js
export default function withNavigation(WrappedComponent) {
return (props) => {
const navigation = useNavigation();
return <Component navigation={navigation} {...props} />
}
}

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

is there any method to re-render the screen to bottom tab nav v5 in react-native

Hy, I'm using react-native bottom tabs navigation the first time I'm having an issue to re-render the screen when I get back to some other tab. I try to use isFocused in useEffect() but it didn't work for me.
const LandScreen = ({navigation}) => {
return (
<Tab.Navigator
tabBarOptions={{
showLabel: false,
activeTintColor: colors.primary,
inactiveTintColor: colors.textHead,
}}
>
<Tab.Screen
name="Home"
component={HomeScreen}
options={{
tabBarIcon: ({focused, color, size}) => {
const icon = focused ? 'home' : 'home';
return <Icon name={icon} color={color} size={size} />;
},
}}
/>
<Tab.Screen
name="Message"
component={ChatStackScreen}
options={{
tabBarIcon: ({focused, color, size}) => {
const icon = focused ? 'chatbox-ellipses' : 'chatbox-ellipses';
return <Icon name={icon} color={color} size={size} />;
},
}}
/>
<Tab.Screen
name="Requests"
component={RequestStackScreen}
options={{
tabBarIcon: ({focused, color, size}) => {
const icon = focused ? 'heart' : 'heart';
return <Icon name={icon} color={color} size={size} />;
},
}}
/>
<Tab.Screen
name="Notifications"
component={NotificationStackScreen}
options={{
tabBarIcon: ({focused, color, size}) => {
const icon = focused ? 'notifications' : 'notifications';
return <Icon name={icon} color={color} size={size} />;
},
}}
/>
<Tab.Screen
name="Profile"
component={ProfileScreenScreen}
options={{
tabBarIcon: ({focused, color, size}) => {
const icon = focused ? 'person' : 'person';
return <Icon name={icon} color={color} size={size} />;
},
}}
/>
</Tab.Navigator>
);
};
Each Tab component having a stack inside of it.
Someone informs me how to rerender the bottom navigation tabs?
If I understand you correctly, you want the screen to re-render when you click on a bottom tab navigation button.
This can be accomplished by either adding the 'focus' event listener via the react navigation docs https://reactnavigation.org/docs/function-after-focusing-screen/
Or by explicitly calling 'navigation.navigate' when a bottom tab is pressed.
Assuming I understand your question, all you want to do is re-render the icons when the tab changes?
React Navigation has docs & an example for this use-case for tab-based navigation here.
Posting the code sample from the docs here for ease-of-access:
import Ionicons from 'react-native-vector-icons/Ionicons';
// (...)
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'ios-information-circle'
: 'ios-information-circle-outline';
} else if (route.name === 'Settings') {
iconName = focused ? 'ios-list-box' : 'ios-list';
}
// You can return any component that you like here!
return <Ionicons name={iconName} size={size} color={color} />;
},
tabBarActiveTintColor: 'tomato',
tabBarInactiveTintColor: 'gray',
})}
>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
If your question meant re-rendering the screen when the tab changes:
Leveraging useEffect with useIsFocused should work when changing tabs. You haven't included your example with those hooks, so I'm not exactly sure how you're using it.
The following snippet should get you up and running with said hooks. In this case, you can run whatever you want inside the useEffect when switching tabs, where the active tab gets triggered.
If for some reason you just want the UI to get updated/persisted based on some data between tab changes you might want to consider using Redux.
import React, { useEffect } from "react";
import { Text, View } from "react-native";
import { NavigationContainer } from "#react-navigation/native";
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { useIsFocused } from "#react-navigation/core";
function HomeScreen() {
const isFocused = useIsFocused();
useEffect(() => {
if (isFocused) {
console.log("HELLO HOME");
}
}, [isFocused]);
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Home!</Text>
</View>
);
}
function SettingsScreen() {
const isFocused = useIsFocused();
useEffect(() => {
if (isFocused) {
console.log("HELLO SETTINGS");
}
}, [isFocused]);
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>Settings!</Text>
</View>
);
}
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeScreen} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}

Resources