cannot read property 'params' of undefined react native - reactjs

I'm coding product detail but can't use the params property
Cannot read property 'params' of undefined
you can see my code here
import React from 'react';
import {Image, Text, View} from 'react-native';
const ProductDetail = ({route, navigation}) => {
const productID = route.params;
return (
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>Product Detail</Text>
</View>
);
};
export default ProductDetail;
Product Item
const CardHeight = 220;
const ProductItem = ({list}) => {
const navigation = useNavigation();
return (
<ScrollView>
{list?.map((item, index) => {
return (
<TouchableOpacity
style={{
borderRadius: sizes.radius,
overflow: 'hidden',
flexDirection: 'row',
}}
onPress={() => {
navigation.navigate('ProductDetails'), {productID: index};
}}>
.....
);
};
export default ProductItem;
MainNavigator
const Stack = createStackNavigator();
const MainNavigator = () => {
return (
<NavigationContainer>
<StatusBar hidden />
<Stack.Navigator>
<Stack.Screen
name="Root"
component={TabDrawer}
options={{
headerShown: false,
useNativeDriver: true,
gestureEnabled: false,
}}
/>
<Stack.Screen
name="ProductDetails"
component={productDetailScreen}
options={{headerShown: false}}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
export default MainNavigator;
I tried to follow the Passing parameters to routes of react navigation without success
productDetail
I don't know where this error comes from can anyone explain to me specifically

Replace this navigation.navigate('ProductDetails'), {productID: index}; with below code:
navigation.navigate('ProductDetails',{productID: index})

Related

How do I handle a combination of stack and page navigation?

I have a Bottom Tab Navigator that has two tabs. The Home tab contains a FlatList of objects. Once the user clicks on an object they should be routed to a page that contains the item that shouldn't show (as a tab) on the bottom tab, yet should show the bottom tab bar. My solution was to use a stack navigator in combination with a bottom tab navigator (shown below) as described in the official docs. However, when I nest the tab navigator inside of the stack navigator, it doesn't display the tab bar on the stack page. The other way around (stack navigator inside the tab navigator) I get an extra tab with the page, which is also an unwanted result.
I must be missing something. If anyone has a better way to route to an item in a list I'd like to hear it as well.
HomeStack (Stack Navigator) with the page Screen:
import { createStackNavigator } from "#react-navigation/stack";
import { ListItem } from "../components/ListItem/ListItem";
export default function StackNavigator() {
const Stack = createStackNavigator();
return (
<Stack.Navigator
screenOptions={{ headerShown: false }}
>
<Stack.Screen name="ListItem" component={ListItem} />
</Stack.Navigator>
);
}
TabsNavigator (BottomTabs) with HomeStack nested in side of it:
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { Home } from "../screens/Home";
import { History } from "../screens/History";
import { FontAwesomeIcon } from "#fortawesome/react-native-fontawesome";
import { faList } from "#fortawesome/free-solid-svg-icons/faList";
import { faClockRotateLeft } from "#fortawesome/free-solid-svg-icons/faClockRotateLeft";
import HomeStack from "./HomeStack";
export default function TabsNavigator() {
const Tab = createBottomTabNavigator();
return (
<Tab.Navigator screenOptions={{ headerShown: false }}>
<Tab.Screen
name="List"
component={Home}
options={{
tabBarIcon: ({ focused }) => (
<FontAwesomeIcon
icon={faList}
style={{ color: focused ? "#317bc1" : "#CCC" }}
/>
),
}}
/>
<Tab.Screen
name="History"
component={History}
options={{
tabBarIcon: ({ focused }) => (
<FontAwesomeIcon
icon={faClockRotateLeft}
style={{ color: focused ? "#317bc1" : "#CCC" }}
/>
),
}}
/>
<Tab.Screen name="ListItem" component={HomeStack} />
</Tab.Navigator>
);
}
App:
import { StyleSheet, View } from "react-native";
import { Provider } from "react-redux";
import configureStore from "./src/redux";
import { NavigationContainer } from "#react-navigation/native";
import Navigator from "./src/routes/TabsNavigator";
export default function App() {
return (
<Provider store={configureStore()}>
<View style={{ flex: 1 }}>
<NavigationContainer>
<Navigator />
</NavigationContainer>
</View>
</Provider>
);
}
Take a reference from this code :
I've just now verified it, fine for me. Go through this code for proper understanding -
import * as React from 'react';
import { Text, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createBottomTabNavigator } from '#react-navigation/bottom-tabs';
import { createNativeStackNavigator } from '#react-navigation/native-stack';
function HomeScreen(props) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text onPress={() => props.navigation.navigate('NextScreen')}>Home!</Text>
</View>
);
}
function NextScreen(props) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text onPress={() => props.navigation.navigate('NextScreen2')}>Next!</Text>
</View>
);
}
function NextScreen2(props) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text onPress={() => props.navigation.navigate('NextScreen3')}>Next2!</Text>
</View>
);
}
function NextScreen3(props) {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text onPress={() => props.navigation.navigate('HomeScreen')}>Next3!</Text>
</View>
);
}
function SettingsScreen() {
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Settings!</Text>
</View>
);
}
const Tab = createBottomTabNavigator();
const stack = createNativeStackNavigator();
const StackNavigator = () => {
return <stack.Navigator option={{headerShown: false}}>
<stack.Screen name="HomeScreen" component={HomeScreen}/>
<stack.Screen name="NextScreen" component={NextScreen}/>
<stack.Screen name="NextScreen2" component={NextScreen2}/>
<stack.Screen name="NextScreen3" component={NextScreen3}/>
</stack.Navigator>
}
function MyTabs() {
return (
<Tab.Navigator>
<Tab.Screen name="Home" component={StackNavigator} />
<Tab.Screen name="Settings" component={SettingsScreen} />
</Tab.Navigator>
);
}
export default function App() {
return (
<NavigationContainer>
<MyTabs />
</NavigationContainer>
);
}
As per your code change your HomeStack (Stack Navigator) likewise: -
import { createStackNavigator } from "#react-navigation/stack";
import { ListItem } from "../components/ListItem/ListItem";
import { Home } from '...<PATH TO HOME SCREEN>...';
const Stack = createStackNavigator();
export default function StackNavigator() {
return (
<Stack.Navigator
screenOptions={{ headerShown: false }}
>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="ListItem" component={ListItem} />
</Stack.Navigator>
);
}
Then change your first Tab likewise : -
<Tab.Screen
name="List"
component={HomeStack}
options={{
tabBarIcon: ({ focused }) => (
<FontAwesomeIcon
icon={faList}
style={{ color: focused ? "#317bc1" : "#CCC" }}
/>
),
}}
/>

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

React Native Stack Navigator Use State

I try to make Share State Data From MyStack Component.
Components of Second Data is Always changed But First Screen is not
Could you tell me about what is problem of this??
And What do i have to Change.
thank you :D
import React ,{ useState } from 'react';
import { Button, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
function First({route , navigation}) {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title={route.params.count.toString()}
onPress={() => navigation.navigate('Second')}/>
</View>
);
}
function Second({route , navigation }) {
route.params.setCount(route.params.count + 1);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title={route.params.count.toString()}
onPress={() => { navigation.goBack()}}
/>
</View>
);
}
const Stack = createStackNavigator();
function MyStack() {
const [count, setCount] = useState(0);
return (
<Stack.Navigator>
<Stack.Screen name="First" component={First} initialParams={{ count: count , setCount : setCount }}/>
<Stack.Screen name="Second" component={Second} initialParams={{ count: count ,setCount : setCount}}/>
</Stack.Navigator>
);
}
export default function App(props) {
return (
<NavigationContainer>
<MyStack props={props}/>
</NavigationContainer>
);
}
The reason for the above not working is the FirstScreen is not being re rendered. You cant dynamically change the intialparams after the screen is rendered.
Even your screen 2 is not working properly, you set it but you see the updated value when you revisit the screen.
The way to fix this is to use the context api which you can use to skip the navigation params.
And always use a hook like useEffect or a buttonclick to update the context otherwise it would keep rerendering which would result in an error.
The code would be like below.
import React, { useState, createContext, useContext } from 'react';
import { Button, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
const appContext = createContext();
function First({ route, navigation }) {
const context = useContext(appContext);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title={context.count.toString()}
onPress={() => navigation.navigate('Second')}
/>
</View>
);
}
function Second({ route, navigation }) {
const context = useContext(appContext);
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Button
title={context.count.toString()}
onPress={() => {
navigation.goBack();
}}
/>
<Button
title="Add to Count"
onPress={() => {
context.setCount(context.count + 1);
}}
/>
</View>
);
}
const Stack = createStackNavigator();
function MyStack() {
const [count, setCount] = useState(0);
const state = { count, setCount };
return (
<appContext.Provider value={state}>
<Stack.Navigator>
<Stack.Screen name="First" component={First} />
<Stack.Screen name="Second" component={Second} />
</Stack.Navigator>
</appContext.Provider>
);
}
export default function App(props) {
return (
<NavigationContainer>
<MyStack props={props} />
</NavigationContainer>
);
}

React Native : how to make context available from App to a Screen when using react navigation?

I am new to RN and try to make a login system, I am storing the email and password (hidden for the sake of brievity of the code) in a global scope in order to use a component to make a call to the server, in short I am trying to keep the email at the App level, and avoid passing from screen to screen.
function App() {
const [email, setEmail] = React.useState('')
const [token,setToken] = React.useState(null)
const AuthenticationContext = React.createContext();
return (
<AuthenticationContext.Provider value={{email,setEmail}}>
<NavigationContainer>
<Stack.Navigator>
>
{token == null ? (
<Stack.Screen name="Home" component={HomeScreen} />
) : (
<Stack.Screen
name="Profile"
component={ProfileScreen}
options={({ route }) => ({ title: route.params.name })}
/>
)}
</Stack.Navigator>
</NavigationContainer>
</AuthenticationContext.Provider>
);
}
And HomeScreen and the LoginForm component which is a grandchild of App component (in which I will set the token):
function HomeScreen({ navigation, route },props ) {
return (
<AuthenticationContext.Consumer>
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<LoginForm {...props} />
{ props.token != null ?
<Button
title="Go to Profile"
onPress={() => navigation.navigate('Profile')}
/>
: null}
</View>
</AuthenticationContext.Consumer>
);
}
However I get the message : AuthenticationContext is not defined
Create the context variable outside of App and make it exportable.
export const AuthenticationContext = React.createContext();
function App() {
return (
<AuthenticationContext.Provider value={{email,setEmail}}>
// Children
</AuthenticationContext.Provider>
)
}
Then on HomeScreen you can use it in the following way
With the useContext hook:
import { AuthenticationContext } from './App';
function HomeScreen (props) => {
const { email, setEmail } = React.useContext(AuthenticationContext);
const onPress = () => {
props.navigation.navigate('Profile');
}
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<LoginForm {...props} />
{ props.token != null ? (
<Button
title="Go to Profile"
onPress={onPress}
/>
) : null
}
</View>
)
}
Or without
import { AuthenticationContext } from './App';
function HomeScreen (props) => {
const onPress = () => {
props.navigation.navigate('Profile');
}
return (
<AuthenticationContext.Consumer>
{({ email, setEmail }) => (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<LoginForm {...props} />
{ props.token != null ? (
<Button
title="Go to Profile"
onPress={onPress}
/>
) : null
}
</View>
}
</AuthenticationContext.Consumer>
)
}

Resources