I created a BottomTabNavigator app with expose, it's my first project with react-native. On the last tab I have a FlatList with a few elements which should open different Views outside the TabNavigation. But when I click on a item in the ListView, I get the following error: TypeError: undefined is not an object (evaluating 'item.navigation.navigate'. This also happens when I try to do it with props, but props also won't work properly in my project.
I'm trying to fix this for two days and I don't know any further. Can anyone please help me with this? In addition, has someone a good guide for navigation and props?
App.tsx
export default function App() {
const isLoadingComplete = useCachedResources();
const colorScheme = useColorScheme();
if (!isLoadingComplete) {
return null;
} else {
return (
<SafeAreaProvider>
<Navigation colorScheme={colorScheme} />
<StatusBar />
</SafeAreaProvider>
);
}
}
index.tsx
export default function Navigation({ colorScheme }: { colorScheme: ColorSchemeName }) {
return (
<NavigationContainer
linking={LinkingConfiguration}
theme={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<RootNavigator />
</NavigationContainer>
);
}
const Stack = createStackNavigator<RootStackParamList>();
function RootNavigator() {
return (
<Stack.Navigator screenOptions={{ headerShown: false }}>
<Stack.Screen name="Root" component={BottomTabNavigator} />
<Stack.Screen name="WebViewImprint" component={WebViewImprint} />
<Stack.Screen name="MenuFlatList" component={MenuFlatList} />
<Stack.Screen name="NotFound" component={NotFoundScreen} options={{ title: 'Oops!' }} />
</Stack.Navigator>
);
}
BottomTabNavigator.tsx
const BottomTab = createBottomTabNavigator<BottomTabParamList>();
export default function BottomTabNavigator() {
const colorScheme = useColorScheme();
return (
{ .... }
<BottomTab.Screen
name="TabMenu"
component={TabMenuNavigator}
options={{
tabBarLabel: "MenĂ¼",
tabBarIcon: ({ color }) => <TabBarIcon name="ios-code" color={color} />,
}}
/>
</BottomTab.Navigator>
);
}
function TabBarIcon(props: { name: string; color: string }) {
return <Ionicons size={30} style={{ marginBottom: -3 }} {...props} />;
}
const TabMenuStack = createStackNavigator<TabMenuParamList>();
function TabMenuNavigator() {
const colorScheme = useColorScheme();
return (
<TabMenuStack.Navigator>
<TabMenuStack.Screen
name="TabMenu"
component={TabMenu}
options={{
header: props => <StacknavigatorImage />,
headerStyle: {
backgroundColor: Colors[colorScheme].barBackgroundColor
}
}}
/>
</TabMenuStack.Navigator>
);
}
MenuFlatListComponent.tsx
const Item = ({ title } : {title: any}) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
export default class MenuFlatList extends React.Component {
render(){
return (
<SafeAreaView style={styles.container}>
<FlatList style={styles.flatList}
data={DATA}
renderItem={({item} : {item: any}) => {
return (
<TouchableWithoutFeedback onPress={() => {
item.navigation.navigate('WebViewImprint');
}}>
<View style={styles.item}>
<Image source={item.icon} style={styles.icon} />
<Item title={item.title} />
</View>
</TouchableWithoutFeedback>
);}
}
keyExtractor={item => item.id}
/>
</SafeAreaView>
);
}
}
You are trying to access navigation which is passed to the MenuFlatList component as a prop you should do like below
<TouchableWithoutFeedback onPress={() => {
this.props.navigation.navigate('WebViewImprint');
}}>
Any screen component in the navigation will receive a 'navigation' props which you can use to navigate to other screens.
Related
The navigation process I want to achieve is (BottomTabNavigator -> RoutinesStackNavigator -> Routines) which I'm using a stack navigator nested inside a bottom tab navigator
When I navigate to the RoutinesStackNavigator it isn't loading the Routines page and I am not sure why.
BottomTabNavigation.tsx
export default function Navigation() {
return (
<>
<SoundProvider>
<TimerProvider>
<NavigationContainer>
<Tab.Navigator
sceneContainerStyle={{ backgroundColor: COLORS.white }}
initialRouteName="Pomodoro"
screenOptions={({ route, navigation }) => ({
tabBarShowLabel: false,
headerShown: false,
tabBarStyle: [styles.tabBar, styles.tabBarBoxShadow],
tabBarIcon: ({ focused }) => {
let rn = route.name;
let iconTag;
// assigning navigation icon based on name of route
if (rn === pomodoroName) {
// assigning JSX Tag
iconTag = (
<PomodoroIcon
size={iconSize}
fillColor={iconColor}
isFocused={focused}
/>
);
} else if (rn === agendaName) {
iconTag = (
<AgendaIcon
size={iconSize}
fillColor={iconColor}
isFocused={focused}
/>
);
} else if (rn === tutorialsName) {
iconTag = (
<TutorialsIcon
size={iconSize}
fillColor={iconColor}
isFocused={focused}
/>
);
} else if (rn === routinesStackNavigatorName) {
iconTag = (
<RoutinesIcon
size={iconSize}
fillColor={iconColor}
isFocused={focused}
/>
);
}
return (
<Pressable
onPress={() => {
// add haptic feedback and navigate to new route
addLightHapticFeedback("light");
navigation.navigate(rn);
}}
>
{iconTag}
</Pressable>
);
},
})}
>
<Tab.Screen name={pomodoroName} component={Pomodoro} />
<Tab.Screen name={agendaName} component={Agenda} />
<Tab.Screen name={tutorialsName} component={Tutorials} />
<Tab.Screen
name={routinesStackNavigatorName}
component={RoutinesStackNavigator}
/>
</Tab.Navigator>
</NavigationContainer>
</TimerProvider>
</SoundProvider>
</>
);
}
RoutinesStackNavigator.tsx
export default function RoutinesStackNavigator() {
const Stack = createStackNavigator();
return (
<>
<SafeAreaView>
<Stack.Navigator initialRouteName="Routines">
<Stack.Screen name="Routines" component={Routines} />
<Stack.Screen name="CreateRoutine" component={CreateRoutine} />
</Stack.Navigator>
</SafeAreaView>
</>
);
}
index.Routines.tsx
export default function Routines() {
const { width } = useWindowDimensions();
return (
<>
<FocusedStatusBar barStyle="light-content" />
<View style={styles.container}>
<View style={styles.row}>
<Text style={styles.title}>{STRINGS.routinesTitle}</Text>
<TouchableOpacity onPress={() => {}}>
<AntDesign name="pluscircle" size={44} color={COLORS.white} />
</TouchableOpacity>
</View>
<View style={[styles.main, { width: width }]}>
<View style={{ height: "72%" }}>
<ScrollView
showsVerticalScrollIndicator={true}
style={{ marginTop: 10, marginBottom: 10 }}
>
<RoutineCard />
<RoutineCard />
</ScrollView>
</View>
<TouchableOpacity style={styles.button} onPress={() => {}}>
<Text style={styles.buttonText}>Start Session</Text>
</TouchableOpacity>
</View>
</View>
</>
);
}
none of the Routines component renders, except the StatusBar color changes. So the component must be rendering but the rest of the content doesn't show up.
You have to remove <SafeAreaView> from RoutinesStackNavigator.tsx
const Stack = createStackNavigator();
export default function RoutinesStackNavigator() {
return (
<Stack.Navigator initialRouteName="Routines">
<Stack.Screen name="Routines" component={Routines} />
<Stack.Screen name="CreateRoutine" component={CreateRoutine} />
</Stack.Navigator>
);
}
I have a screen with the following:
function Intereset ({ navigation }) {
function ReturnMyFunction () {
if (!var.length) {
return ( <NoILikes /> )
} else {
return (
<FlatList
data={Ilike}
keyExtractor={item => item._id}
ItemSeparatorComponent={() => <Divider />}
renderItem={UserRow}
/>
)
}
}
return ( <ReturnILikeOrNoILike /> )
}
export default Interest
Here is my UserRow component below:
const UserRow = ({ item, navigation }) => (
<TouchableOpacity onPress={() => navigation.navigate("ProfileDetailScreenSingle", { userID: item.likeTo })}>
<View style={styles.row}>
<Image style={styles.avatar}
resizeMode={"cover"}
source={{ uri: item.likeToProfileImage }}
/>
<View style={styles.textContainer}>
<Text style={styles.name}>{item.likeToName}, <Text>{item.likeToAge}</Text></Text>
</View>
<Text style={styles.viewProfileText}>View Profile</Text>
<AntDesign name="right" size={14} color="black" />
</View>
</TouchableOpacity>
)
When I click on the UserRow to navigate I get the following issue.
I'm using useNavigation to redirect screen in stack:
import { useNavigation } from "#react-navigation/native";
const UserRow = ({ item }) => {
const navigation = useNavigation()
const onGoToProfileDetailScreenSingle = () => navigation.navigate("ProfileDetailScreenSingle", { userID: item.likeTo })
return (
<TouchableOpacity onPress={onGoToProfileDetailScreenSingle}>
...
</TouchableOpacity>
);
}
const renderItem = ({ item }) => <UserRow item={item} />
return (
<FlatList
data={Ilike}
renderItem={renderItem}
...
/>
)
Use onPress handler instead and perform navigation on the screen.
const UserRow = ({ item, onPress }) => (
<TouchableOpacity onPress={onPress}>
...
</TouchableOpacity>
);
const renderItem = ({ item }) => {
return (
<UserRow
item={item}
onPress={() => navigation.navigate(...)}
/>
);
};
<FlatList
data={Ilike}
renderItem={renderItem}
...
/>
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>
);
}
After not succeeding in solving my problem here, I had to create a new question with all my classes in it.
I am new to react native and have a problem figuring out how to navigate from one class to another one with passing parameters and would appreciate your help.
All I want to do is:
SessionCreate with flatlist containing CustomButton
Navigate from SessionCreate to ItemConfig by clicking CustomButton
Pass parameter "element" to ItemConfig
Show content of parameter passed in ItemConfig
With this setup an empty "element" is passed as parameter to the ItemConfigScreen (but no error occurs):
app.js:
//Views
function Home({ navigation }) {
return (
<HomeScreen />
);
}
function Session({ navigation }) {
return (
<SessionScreen />
);
}
//subviews
function SessionCreate({ navigation }) {
return (
<SessionCreateScreen />
);
}
function ItemConfig({ navigation }) {
return (
<ItemConfigScreen />
);
}
//navigation stacks
const SessionStack = createStackNavigator();
function SessionStackScreen({ navigation }) {
return (
<SessionStack.Navigator>
<SessionStack.Screen
name="Session"
component={Session}
options={{ tabBarLabel: 'Session!' }}
/>
<SessionStack.Screen
name="SessionCreate"
component={SessionCreate}
options={{ tabBarLabel: 'SessionCreate!' }}
/>
<SessionStack.Screen
name="ItemConfig"
component={ItemConfig}
options={{ tabBarLabel: 'ItemConfig!' }}
/>
</SessionStack.Navigator>
);
}
//Navbar Bottom
const Tab = createBottomTabNavigator();
function App() {
return (
<View style={[theme.colcontainer, { flexDirection: "column" }]} >
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused ? 'home' : 'home-outline';
} else if (route.name === 'Session') {
iconName = focused ? 'book' : 'book-outline';
}
// dynamic ionicon
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Session" component={SessionStackScreen} />
</Tab.Navigator>
</NavigationContainer>
</View >
);
}
export default App;
SessionScreen.js:
function SessionScreen() {
const navigation = useNavigation();
return (
<View style={[theme.container, { flexDirection: "column" }]} >
<View>
<TouchableOpacity onPress={() => navigation.navigate('SessionCreate')}>
<Text >Create Session</Text>
</TouchableOpacity>
</View>
</View >
);
}
export default SessionScreen;
SessionCreateScreen.js:
//data
const sessionElements = [
{
id: "1",
title: "title1"
}
];
function SessionCreateScreen() {
const navigation = useNavigation()
const renderItemConfiguration = ({ item }) => (
<CustomButton element={item.title} onPress={() => navigation.navigate('ItemConfig', { element: 'item.title' })} />
);
return (
<View style={{ flexDirection: "column", flex: 2}} >
<SafeAreaView >
<FlatList
data={sessionElements}
renderItem={renderItemConfiguration}
keyExtractor={(item) => item.id}
/>
</SafeAreaView>
</View >
);
}
export default SessionCreateScreen;
ItemConfigScreen.js:
const element = "";
function ItemConfigScreen() {
return (
<ScrollView >
<View style={{ flexDirection: "column", flex: 2}} >
<Text>Configure {element} here</Text>
</View >
</ScrollView>
);
}
export default ItemConfigScreen;
Any help is appreciated.
To get parameters in ItemConfigScreen you have to use the useRoute hook from the react-navigation package.
you can read more about it here useRoute
import {useRoute} from '#react-navigation/native';
function ItemConfigScreen() {
const route = useRoute();
const element = route.params?.element;
return (
<ScrollView >
<View style={{ flexDirection: "column", flex: 2}} >
<Text>Configure {element} here</Text>
</View >
</ScrollView>
);
}
There is also a mistake in your onPress navigation call in CustomButton, instead of 'item.title' you will have to pass ${item.title} then only actual data will be passed. JS Template Literals
<CustomButton element={item.title} onPress={() => navigation.navigate('ItemConfig', { element: `${item.title}` })} />
I am trying to make drawer navigation from component. But all time its giving me error "TypeError: Cannot read property 'navigate' of undefined" when click on menu ->profile link. Please suggest where I am wrong.
component/MenuPage.js
import React from 'react';
import {SafeAreaView, View, StyleSheet, Image, Text, Linking,} from 'react-native';
import { DrawerContentScrollView, DrawerItemList, DrawerItem,} from '#react-navigation/drawer';
const image = { uri: "" };
const CustomSidebarMenu = (props) => {
return (
<SafeAreaView style={[styles.LandingBodyColor, {flex: 1, paddingHorizontal:10}]}>
<View style={[styles.LandingMain, {marginVertical:10,}]}>
<Image source={image} style={{width:100, height:100, borderRadius:100,}} />
</View>
<View>
<Text style={[styles.TextCenter, {fontSize:18, color: '#fff', paddingBottom:10,}]}>Welcome Back.</Text>
</View>
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
<View style={[styles.customItem]}>
<Text style={{marginRight:10,}}>
<FontAwesome name="user" style={[styles.MenuIcon, styles.VectorIconWhiteColor]}/>
</Text>
<Text style={[styles.WhiteColor]} onPress={() => navigation.navigate(Profile) >My Profile</Text>
</View>
</DrawerContentScrollView>
</SafeAreaView>
);
};
export default CustomSidebarMenu;
App.js
export default function App() {
return (
<NavigationContainer>
<Drawer.Navigator
drawerContentOptions={{
activeBackgroundColor: '#e40046',
}}
drawerContent={(props) => <CustomSidebarMenu {...props} />}
screenOptions={{
headerShown: false,
}}
>
<Drawer.Screen name="Landing Page" component={AllScreenStack}
options={() => ({
drawerLabel: () => null,
title: undefined,
drawerIcon: () => null,
headerTitleStyle: {
backgroundColor:'red',
},
})}
/>
</Drawer.Navigator>
</NavigationContainer>
);
}
const Stack = createStackNavigator();
const Drawer = createDrawerNavigator();
Try with props like this
props.navigation.navigate('Profile')