React navigation 5: Undefined is not an object - javascript

On my UserProductsScreen.js file, I passed a parameter id so that I can pick it up on my navigation:
const UserProductsScreen = props => {
const userProducts = useSelector(state => state.products.userProducts);
// Navigate to EditProduct
const editProductHandler = (id) => {
props.navigation.navigate('EditProduct', { productId: id })
};
And then, inside my navigator.js file, I tried to check if it exist so that I can decide which header title to show either "Add Product" or "Edit Product":
<AdminNavigator.Screen
name="EditProduct"
component={EditProductScreen}
options={({ route }) => {
const productId = route.params.productId;
return {
title: productId ? "Edit Product" : "Add Product"
};
}}
/>
I tested this if it receive the productId and it did but then, I received this error when click on the plus icon: TypeError: undefined is not an object (evaluating 'route.params.productId')
I am not sure what it means but I am simply checking if it exist so that I can change the header dynamically.
For the record here's my complete navigation:
import React from 'react';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import { createDrawerNavigator } from '#react-navigation/drawer';
import { Platform } from 'react-native';
import { Ionicons } from '#expo/vector-icons';
import ProductsOverviewScreen from '../screens/shop/ProductsOverviewScreen';
import ProductDetailScreen from '../screens/shop/ProductDetailScreen';
import CartScreen from '../screens/shop/CartScreen';
import OrdersScreen from '../screens/shop/OrdersScreen';
import UserProductsScreen from '../screens/user/UserProductsScreen';
import EditProductScreen from '../screens/user/EditProductScreen';
import Colors from '../constants/Colors';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import HeaderButton from '../components/UI/HeaderButton';
const ProductsNavigator = createStackNavigator();
const ProductsNavMenu = () => {
return (
<ProductsNavigator.Navigator
screenOptions={{
headerStyle: {
backgroundColor: Platform.OS === 'android' ? Colors.primary : ''
},
headerTintColor: Platform.OS === 'android' ? 'white' : Colors.primary,
headerTitleStyle: {
fontSize: 17,
fontFamily: 'poppins-bold'
},
headerBackTitleStyle: {
fontFamily: 'poppins-regular'
}
}}>
<ProductsNavigator.Screen
name="Products"
component={ProductsOverviewScreen}
options={({ navigation }) => {
return {
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title="Cart"
iconName={Platform.OS === 'android' ? 'md-cart' : 'ios-cart'}
onPress={() => navigation.navigate('Cart')}
/>
</HeaderButtons>
),
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Menu'
iconName={Platform.OS === 'android' ? 'md-menu' : 'ios-menu'}
onPress={() => {
navigation.toggleDrawer();
}}
/>
</HeaderButtons>
)
};
}}
/>
<ProductsNavigator.Screen
name="ProductDetail"
component={ProductDetailScreen}
options={({ route }) => {
const productTitle = route.params.productTitle;
return {
title: productTitle
};
}}
/>
<ProductsNavigator.Screen
name="Cart"
component={CartScreen}
/>
</ProductsNavigator.Navigator>
);
};
// Create a separate stack navigation
// for orders
const OrdersNavigator = createStackNavigator();
const OrdersNavMenu = () => {
return (
<OrdersNavigator.Navigator
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: Platform.OS === 'android' ? Colors.primary : ''
},
headerTintColor: Platform.OS === 'android' ? 'white' : Colors.primary,
headerTitleStyle: {
fontSize: 17,
fontFamily: 'poppins-bold'
},
headerBackTitleStyle: {
fontFamily: 'poppins-regular'
}
}}
>
<OrdersNavigator.Screen
name="Orders"
component={OrdersScreen}
options={({ navigation }) => {
return {
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Menu'
iconName={Platform.OS === 'android' ? 'md-menu' : 'ios-menu'}
onPress={() => {
navigation.toggleDrawer();
}}
/>
</HeaderButtons>
)
};
}}
/>
</OrdersNavigator.Navigator>
);
};
// Create a separate stack navigation
// for userProductsScreen
const AdminNavigator = createStackNavigator();
const AdminNavMenu = () => {
return (
<AdminNavigator.Navigator
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: Platform.OS === 'android' ? Colors.primary : ''
},
headerTintColor: Platform.OS === 'android' ? 'white' : Colors.primary,
headerTitleStyle: {
fontSize: 17,
fontFamily: 'poppins-bold'
},
headerBackTitleStyle: {
fontFamily: 'poppins-regular'
}
}}
>
<AdminNavigator.Screen
name="UserProducts"
component={UserProductsScreen}
options={({ navigation }) => {
return {
title: "User Products",
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='User Products'
iconName={Platform.OS === 'android' ? 'md-list' : 'ios-list'}
onPress={() => {
navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Add'
iconName={Platform.OS === 'android' ? 'md-create' : 'ios-create'}
onPress={() => {
navigation.navigate('EditProduct');
}}
/>
</HeaderButtons>
)
};
}}
/>
<AdminNavigator.Screen
name="EditProduct"
component={EditProductScreen}
options={({ route }) => {
const productId = route.params.productId;
return {
title: productId ? "Edit Product" : "Add Product"
};
}}
/>
</AdminNavigator.Navigator>
);
};
const ShopNavigator = createDrawerNavigator();
const ShopNavMenu = () => {
return (
<NavigationContainer>
<ShopNavigator.Navigator>
<ShopNavigator.Screen
name="Products"
component={ProductsNavMenu}
options={{
drawerIcon: ({ focused, size }) => (
<Ionicons
name={Platform.OS === 'android' ? 'md-cart' : 'ios-cart'}
size={23}
color={focused ? '#7cc' : '#ccc'}
/>
)
}}
/>
<ShopNavigator.Screen
name="Orders"
component={OrdersNavMenu}
options={{
drawerIcon: ({ focused, size }) => (
<Ionicons
name={Platform.OS === 'android' ? 'md-list' : 'ios-list'}
size={23}
color={focused ? '#7cc' : '#ccc'}
/>
)
}}
/>
<ShopNavigator.Screen
name="Admin"
component={AdminNavMenu}
options={{
drawerIcon: ({ focused, size }) => (
<Ionicons
name={Platform.OS === 'android' ? 'md-create' : 'ios-create'}
size={23}
color={focused ? '#7cc' : '#ccc'}
/>
)
}}
/>
</ShopNavigator.Navigator>
</NavigationContainer>
);
};
export default ShopNavMenu;
Upate: So I tried to use setParams on UserProductsScreen where the magic is happening. I use useEffect to set an initial productId to blank:
import React, { useEffect } from 'react';
import { CommonActions } from '#react-navigation/native';
const UserProductsScreen = props => {
const userProducts = useSelector(state => state.products.userProducts);
// Navigate to EditProduct
const editProductHandler = (id) => {
props.navigation.navigate('EditProduct', { productId: id })
};
// Set initial productId params not until the editProductHandler is triggerred
useEffect(() => {
props.navigation.dispatch(CommonActions.setParams({ productId: '' }));
}, []);
// dispatch
const dispatch = useDispatch();
And then on my navigation I tried to catch the the productId via my headerRight:
<AdminNavigator.Screen
name="UserProducts"
component={UserProductsScreen}
options={({ navigation, route }) => {
const productId = route.params.productId;
return {
title: "User Products",
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='User Products'
iconName={Platform.OS === 'android' ? 'md-list' : 'ios-list'}
onPress={() => {
navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Add'
iconName={Platform.OS === 'android' ? 'md-create' : 'ios-create'}
onPress={() => {
navigation.navigate('EditProduct');
}}
/>
</HeaderButtons>
)
};
}}
/>
But this affects the id also on the regular navigation. Now the error is above is also showing when clicking the edit button.

The problem is here
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Add'
iconName={Platform.OS === 'android' ? 'md-create' : 'ios-create'}
onPress={() => {
navigation.navigate('EditProduct');
}}
/>
</HeaderButtons>
When you are navigating from the header right button you are not passing any parameters.
If you do not have access to route params at this point, you will have to use the setParams to set the parameters after you navigate to the page.
You can refer the doc
https://reactnavigation.org/docs/navigation-prop#setparams
Update :
so when you navigate wihout passing anything the route.params is undefined
when you access a property if undefined it throws an error
The way to validate this is to use the Optional Chaining like below
const productId = route.params?.productId;
This will set the value only if params is available.

I had this issue when my first screen loads. The params were undefined and I fixed it by using the initialParams option to set the params for my first screen.
<Stack.Screen
component={LCWebView}
options={{
title: 'Awesome app',
myopt: 'test',
}}
initialParams={{ url: url_goes_here }}
/>

Related

How do I add routes to my react native app navigation, but keep it out of my tab navigation?

I've watched tutorials and looked at the docs however I still don't get it, I need someone to explain using my situation/code.
My navigation setup is similar to Instagram.
Bottom tab navigation has Home/Search/Inventory/Profile.
Here is my navigation file:
import { createBottomTabNavigator } from "#react-navigation/bottom-tabs";
import { PresenceTransition, Text, View } from "native-base";
import Home from "../../screens/Home";
import Profile from "../../screens/Profile";
import InventoryIcon from "../svg/InventoryIcon";
import SearchIcon from "../svg/SearchIcon";
import UserIcon from "../svg/UserIcon";
import HomeIcon from "../svg/HomeIcon";
import Search from "../../screens/Search";
import Inventory from "../../screens/Inventory";
import authStore from "../../zustand/authStore";
import Login from "../../screens/Login/Login";
import { SafeAreaView } from "react-native-safe-area-context";
import { TouchableOpacity } from "react-native";
import userDataStore from "../../zustand/userDataStore";
import Avatar from "../Avatar";
const Tab = createBottomTabNavigator();
export default function BottomNavigation() {
const currentUser = authStore((state) => state.currentUser);
if (!currentUser)
return (
<SafeAreaView>
<Login />
</SafeAreaView>
);
return (
<Tab.Navigator
tabBar={(props) => <TabBar {...props} />}
initialRouteName="Home"
screenOptions={({ route }) => ({
headerShown: false,
tabBarStyle: {
elevation: 0,
borderTopWidth: 0,
backgroundColor: "#2563eb",
},
tabBarShowLabel: false,
})}
>
<Tab.Screen name="Home" component={Home} />
<Tab.Screen name="Search" component={Search} />
<Tab.Screen name="Inventory" component={Inventory} />
<Tab.Screen name="Profile" component={Profile} />
</Tab.Navigator>
);
}
function TabBar({ state, descriptors, navigation }) {
const currentUser = authStore((state) => state.currentUser);
const userData = userDataStore((state) => state.userData);
return (
<View className="bg-blue-600 py-4" style={{ flexDirection: "row" }}>
{state.routes.map((route, index) => {
const { options } = descriptors[route.key];
const isFocused = state.index === index;
let params;
if (route.name === "Profile") {
params = {
uid: currentUser?.uid,
};
}
const onPress = () => {
const event = navigation.emit({
type: "tabPress",
target: route.key,
});
if (route.name === "Profile" && !event.defaultPrevented) {
navigation.navigate(route.name, params);
return;
}
if (!isFocused && !event.defaultPrevented) {
navigation.navigate(route.name, params);
}
};
const onLongPress = () => {
navigation.emit({
type: "tabLongPress",
target: route.key,
});
};
return (
<TouchableOpacity
accessibilityRole="button"
accessibilityState={isFocused ? { selected: true } : {}}
accessibilityLabel={options.tabBarAccessibilityLabel}
testID={options.tabBarTestID}
onPress={onPress}
onLongPress={onLongPress}
style={{ flex: 1 }}
>
<View className="relative flex w-full flex-1 items-center justify-center">
<TabIcon
currentUserProfilePicture={userData?.profilePicture}
name={route.name}
/>
<PresenceTransition
visible={isFocused}
initial={{ opacity: 0, scale: 0 }}
animate={{
opacity: 1,
scale: 1,
transition: {
duration: 200,
},
}}
className="absolute -bottom-7 h-4 w-4 rounded-sm bg-white"
/>
</View>
</TouchableOpacity>
);
})}
</View>
);
}
function TabIcon({
name,
currentUserProfilePicture,
}: {
name: string;
currentUserProfilePicture: undefined | null | string;
}) {
if (name === "Home") {
return <HomeIcon svgClassName="text-white w-6 h-6" />;
}
if (name === "Search") {
return <SearchIcon svgClassName="text-white w-6 h-6" />;
}
if (name === "Collections") {
return <CollectionsIcon svgClassName="text-white w-6 h-6" />;
}
if (name === "Profile") {
return currentUserProfilePicture ? (
<Avatar
url={currentUserProfilePicture}
alt="Your profile"
className="h-6 w-6"
/>
) : (
<UserIcon svgClassName="text-white w-6 h-6" />
);
}
return null;
}
Now, the problem is I want to add a Settings screen. This screen shouldn't be in the bottom tab bar, it should only be navigated to from the Profile screen. If I add it to the navigation, it is automatically added.
I could do something like:
const hiddenRoutes = ["Settings"]
if (hiddenRoutes.includes(route.name) {
return;
}
But this seems quite hacky to me, I feel its wrong.
How should I best declare routes, but keep them out of the bottom tab navigation?
You will want to create another navigation layer for your profile screen
To make the stack navigation for the profile screen:
const ProfileStack = createNativeStackNavigator<ProfileStackParamList>();
function ProfileNavigator() {
return (
<ProfileStack.Navigator >
<ProfileStack.Screen
name={"Profile"}
component={Profile}
options={() => ({
title: "Profile",
})}
/>
<ProfileStack.Screen
name={"Settings"}
component={Settings}
options={() => ({
title: "Settings",
})}
/>
</ProfileStack.Navigator>
);
}
Then in where you are making your Tab navigator use this stack navigation in the component
<Tab.Screen name="Profile" component={ProfileNavigator} />

Detect internet connection in react native anywhere in screens

I'm confused on how can I show my customDialog whenever I don't have Internet connection to my app, Currently, I've only managed to show my customDialog within LoginScreen , I just want to show my custom dialog from different screen not only on LoginScreen whenever I don't have Internet connection, what is the best way to implement it ? should I put CustomDialog each of the screen?
Someone have the idea to implement this? need help
screens/LoginScreen
import NetInfo from "#react-native-community/netinfo";
import CustomAlert from '../components/CustomAlert';
const LoginScreen = ({navigation}) => {
const [modalVisible, setModalVisible] = useState(false);
const [internetCon, setNet] = useState(true);
React.useEffect(()=>{
checkInternetConnection();
}, [])
function checkInternetConnection() {
NetInfo.addEventListener(state =>{
if (state.isConnected==false){
setModalVisible(true);
setNet(false);
console.log("Connection types", state.type);
console.log("Your device appears to have no internet connectivity. Please check your connection settings and try again");
}
else{
console.log("Connected to internet?", state.isConnected);
}
});
}
return (
<ScrollView style={styles.container} >
{internetCon ===false ? //Check if Internet Existt
<CustomAlert
modalVisible={modalVisible}
setModalVisible={setModalVisible}
title={'Message'}
message={'Your device appears to have no internet connectivity. Please check your connection settings and try again'}
buttons={[{
text: 'Retry',
func: () => {checkInternetConnection();}
}]}
/>
:null}
</ScrollView>
);
};
export default LoginScreen;
navigation/AppStack.js
const Stack = createStackNavigator();
const Tab = createBottomTabNavigator();
const FeedStack = ({navigation}) => (
<Stack.Navigator
screenOptions={{
headerShown: false
}}>
<Stack.Screen
name="test"
component={HomeScreen}
options={{
headerTitleAlign: 'center',
headerTitleStyle: {
color: '#2e64e5',
fontFamily: 'Kufam-SemiBoldItalic',
fontSize: 18,
},
headerStyle: {
shadowColor: '#fff',
elevation: 0,
},
headerRight: () => (
<View style={{marginRight: 10}}>
<FontAwesome5.Button
name="plus"
size={22}
backgroundColor="#fff"
color="#2e64e5"
onPress={() => navigation.navigate('AddPost')}
/>
</View>
),
}}
/>
<Stack.Screen
name="HomeProfile"
component={ProfileScreen}
options={{
title: '',
headerTitleAlign: 'center',
headerStyle: {
backgroundColor: '#fff',
shadowColor: '#fff',
elevation: 0,
},
headerBackTitleVisible: false,
headerBackImage: () => (
<View style={{marginLeft: 15}}>
<Ionicons name="arrow-back" size={25} color="#2e64e5" />
</View>
),
}}
/>
</Stack.Navigator>
);
const AppStack = () => {
const getTabBarVisibility = (route) => {
const routeName = route.state
? route.state.routes[route.state.index].name
: '';
if (routeName === 'Chat') {
return false;
}
return true;
};
return (
<Tab.Navigator
screenOptions={{
headerShown: false
}}
tabBarOptions={{
activeTintColor: '#2e64e5',
}}>
<Tab.Screen
name="Home"
component={FeedStack}
options={({route}) => ({
tabBarLabel: 'Home',
// tabBarVisible: route.state && route.state.index === 0,
tabBarIcon: ({color, size}) => (
<MaterialCommunityIcons
name="home-outline"
color={color}
size={size}
/>
),
})}
/>
<Tab.Screen
name="Messages"
component={MessageStack}
options={({route}) => ({
tabBarVisible: getTabBarVisibility(route),
tabBarIcon: ({color, size}) => (
<Ionicons
name="chatbox-ellipses-outline"
color={color}
size={size}
/>
),
})}
/>
</Tab.Navigator>
);
};
export default AppStack;
navigation/AuthProvider.js
export const AuthContext = createContext();
export const AuthProvider = ({children}) => {
const [user, setUser] = useState(null);
const [modalVisible, setModalVisible] = useState(false);
return (
<AuthContext.Provider
value={{
user,
setUser,
googleLogin: async () => {
try {
// Get the users ID token
const Token = await GoogleSignin.signIn();
const {idToken} = Token;
console.log("Processing");
console.log("ok");
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
await auth().signInWithCredential(googleCredential)
.catch(error => {
console.log('Something went wrong with sign up: ', error);
});
} catch(error) {
// console.log('Something went wrong with sign up: ', error);
}
},
}}>
{children}
</AuthContext.Provider>
);
};
Yo can try react-navigation modal: https://reactnavigation.org/docs/modal/

Need help using goBack() in react native

I am having trouble with the back button in the stack navigator. I am getting an error saying undefined is not an object (evaluating '_this.props'). if it works it goes back to a different tab in the bottom stack navigator. Like if I click on book a session and then I go to home and click back it will take me to book a session no matter what. Please help.
This is what I have so far:
function HomeScreen({ navigation }) {
return (
<WebView
source={{
uri: 'https://www.stoodnt.com/'
}}
style={{ marginTop: -120 }}
/>
);
}
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home"
component={HomeScreen}
options={{
headerLeft: () => (
<HeaderBackButton
onPress={() => this.props.navigation.goBack(null)}
/>
),
}}
/>
</HomeStack.Navigator>
);
}
Bottom Navigator:
const Tab = createBottomTabNavigator();
export default function App() {
return (
<NavigationContainer>
<Tab.Navigator
screenOptions={({ route }) => ({
tabBarIcon: ({ focused, color, size }) => {
let iconName;
if (route.name === 'Home') {
iconName = focused
? 'ios-home'
: 'ios-home';
} else if (route.name === 'Book Session') {
iconName = focused ? 'ios-calendar' : 'ios-calendar';
}
else if (route.name === 'Blogs') {
iconName = focused ? 'ios-bookmark' : 'ios-bookmark';
}
else if (route.name === 'Online Courses') {
iconName = focused ? 'ios-book' : 'ios-book';
}
else if (route.name === 'Classes') {
iconName = focused ? 'ios-desktop' : 'ios-desktop';
}
return <Ionicons name={iconName} size={40} color={'orange'} />;
},
})}
tabBarOptions={{
activeTintColor: '#000000',
inactiveTintColor: '#616161',
labelStyle: {
fontSize: 10,
},
style: {
backgroundColor: '#F7F7F7',
},
}}
>
<Tab.Screen name="Home" component={HomeStackScreen} />
<Tab.Screen name="Book Session" component={BookStackScreen} />
<Tab.Screen name="Blogs" component={BlogStackScreen} />
<Tab.Screen name="Online Courses" component={OnlineStackScreen} />
<Tab.Screen name="Classes" component={ClassesStackScreen} />
</Tab.Navigator>
</NavigationContainer>
);
}
try the below code :
function HomeScreen(props) {
return (
<WebView
source={{
uri: 'https://www.stoodnt.com/'
}}
style={{ marginTop: -120 }}
/>
);
}
const HomeStack = createStackNavigator();
function HomeStackScreen() {
return (
<HomeStack.Navigator>
<HomeStack.Screen name="Home"
component={HomeScreen}
options={({ route, navigation }) => ({
headerLeft: () => (
<HeaderBackButton
onPress={() => navigation.goBack(null)}
/>
),
})}
/>
</HomeStack.Navigator>
);
}```
In functional components, you do not have access to "this". You should either pass navigation from the screen options or use useNavigation.
<HomeStack.Screen
...
options={({navigation}) => (
headerLeft: () => (
// you can use navigation.goBack() here
)
)}
or
// this should be added right under function HomeStackScreen,
// right above the return and you will be able to use it in the screens
const navigation = useNavigation()

Passing data from a functional component to a parent class component react native

So I have a tab navigation in my app and I am trying to scan a UPC number from on of the tab screens which is called ScanScreen and send it to the homeScreen where I have a search bar and I want the upc number to show up there once the navigation has been done. So far I am able to scan the upc number and navigate to the homeScreen but for some reason I am not able to send the upc number. I have tried many ways ex. navigation.navigate('HomeScreen',{upc: data}) and the getParam() on the home screen but nothing works.
Obj = scan the upc and then successfully navigate to the homeScreen tab and show the number on the searchBar
export default class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.state = { search: '', isLoading: true };
this.products = [];
}
componentDidMount() {
db.collection("products")
.get()
.then(querySnapshot => {
const data = querySnapshot.docs.map(doc => doc.data());
this.setState({
products: data,
isLoading: false,
dataSource: data
},
function () {
this.products = data;
}
);
}).catch(error => {
console.error(error);
});
}
GetFlag(ingredients, status) {
var Ingredients = Object.values(ingredients);
for (var i = 0; i < Ingredients.length; i++) {
if (Ingredients[i].Status == status)
return true;
}
}
search = text => {
console.log(text);
};
clear = () => {
this.search.clear();
};
SearchFilterFunction(text) {
const newData = this.products.filter(function (item) {
const itemData = item.Name ? item.Name.toUpperCase() :
''.toUpperCase();
const upcData = item.UPC;
const textData = text.toUpperCase();
if (itemData.indexOf(textData) > -1)
return true;
else if (upcData.toString().indexOf(textData) > -1)
return true;
else
return false;
});
this.setState({
dataSource: newData, search: text,
});
}
ListViewItemSeparator = () => {
return (
<View
style={{
height: 0.4,
width: '100%',
backgroundColor: '#141313',
}}
/>
);
};
render() {
const { navigate } = this.props.navigation;
if (this.state.isLoading) {
return (
<View style={{ flex: 1, paddingTop: 21 }}>
<ActivityIndicator />
</View>
);
}
const upc = navigate.getParams('upcCode')
console.log(upc)
return (
<View style={styles.viewStyle}>
<SearchBar round
searchIcon={{ size: 25 }}
onChangeText={text => this.SearchFilterFunction(text)} onClear={text => this.SearchFilterFunction('')}
placeholder="Type Here to Search..." value={this.state.search}
/>
{
this.state.search.trim() != "" ?
<FlatList data={this.state.dataSource}
ItemSeparatorComponent={this.ListViewItemSeparator}
renderItem={({ item }) => (
<View style={styles.container}>
<TouchableHighlight onPress={() => this.props.navigation.navigate('Summary', {
ProductObject: item
})}
>
<View >
{
this.GetFlag(item.ingredients, 'Haram') == true ?
<Image source={images['Haram']} style={styles.imageView} /> :
this.GetFlag(item.ingredients, 'Halal') == true ?
<Image source={images['Halal']} style={styles.imageView} /> :
this.GetFlag(item.ingredients,'Doubtful') == true ?
<Image source={images['Doubtful']} style={styles.imageView} /> :
null
}
<Text style={styles.baseText}>
<Text style={styles.titleText} >
{item.Name}{'\n'}{'\n'}
</Text>
</Text>
</View>
</TouchableHighlight>
</View>
)}
enableEmptySections={true}
style={{ marginTop: 11 }}
keyExtractor={(item, index) => index.toString()}
/> : <FlatList></FlatList>
}
</View>
);
}
}
export default function ScanScreen({navigation}) {
const [hasPermission, setHasPermission] = useState(null);
const [scanned, setScanned] = useState(false);
useEffect(() => {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === 'granted');
})();
}, []);
const handleBarCodeScanned = ({ type, data }) => {
setScanned(true);
navigation.navigate("Home",{
upcCode: data
})
};
if (hasPermission === null) {
return <Text>Requesting for camera permission</Text>;
}
if (hasPermission === false) {
return <Text>No access to camera</Text>;
}
return (
<View
style={{
flex: 1,
flexDirection: 'column',
justifyContent: 'flex-end',
}}>
<BarCodeScanner
onBarCodeScanned={scanned ? undefined : handleBarCodeScanned}
style={StyleSheet.absoluteFillObject}
/>
{scanned && <Button title={'Tap to Scan Again'} onPress={() => setScanned(false)} />}
</View>
);
}
Below is my AppNavigator.JS code if it helps seeing the navigation
function MainTabNavigator() {
return (
<Tab.Navigator tabBarOptions={{
activeTintColor: 'red',
inactiveTintColor: 'gray'
}}
screenOptions={({ route }) => ({
tabBarIcon: ({ color, size }) => {
let iconName;
let tabBarVisible = true;
if (route.name == 'Home') {
iconName = 'md-home'
} else if (route.name == 'Scan') {
iconName = 'md-camera'
} else if (route.name == 'Menu') {
iconName = "md-menu"
}
else if (route.name == 'Login') {
iconName = "md-log-in"
}
return <Ionicons name={iconName} color={color} size={size} />
}
})}
>
<Tab.Screen
name='Home'
component={HomeScreen}
options={({route}) => ({
title: "Search for Products",
headerBackTitleVisible: false,
headerStyle: {
backgroundColor: '#1c2845',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize:22,
fontFamily: 'DamascusLight'
}
})}
/>
<Tab.Screen
name='Login'
component={LoginScreen}
/>
<Tab.Screen name='Scan' component={ScanScreen} />
<Tab.Screen name='Menu' component={SearchScreen} />
</Tab.Navigator>
)
}
function MainStackNavigator() {
return (
<NavigationContainer>
<Stack.Navigator>
{/* <Stack.Screen name='Login' component={LoginScreen} /> */}
<Stack.Screen name='HomeScreen' component={MainTabNavigator}
options={({ route }) => ({
title: 'Search for Products',
headerBackTitleVisible: false,
headerStyle: {
backgroundColor: '#1c2845',
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 22,
fontFamily: "DamascusLight"
},
}
)} />
<Stack.Screen name='RegistrationScreen' component={RegistrationScreen} />
<Stack.Screen
name="Summary"
component={SummaryScreen}
options={({ route }) => ({
headerTitle: () =>
<View style={styles.header}>
<Text numberOfLines={1} style={styles.title}>{route.params.title}</Text>
<Text numberOfLines={1} style={styles.subtitle}>{route.params.subtitle}</Text>
</View>,
headerBackTitleVisible: false,
headerStyle: {
backgroundColor: '#1c2845',
height: 100
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 12,
fontFamily: "DamascusLight"
},
}
)}
/>
<Stack.Screen
name="ScanSummary"
component={ScanSummary}
// options={({route}) => ({
// headerTitle: () =>
// <View style={styles.header}>
// <Text numberOfLines={1}>{route.params.title}</Text>
// <Text numberOfLines={1}>{route.params.subtitle}</Text>
// </View>,
// headerStyle: {
// backgroundColor: '#1c2845',
// height: 100
// },
// headerTintColor: '#fff',
// headerTitleStyle: {
// fontSize: 12,
// fontFamily: "DamascusLight"
// },
// })}
/>
</Stack.Navigator>
</NavigationContainer>
)
}
export default MainStackNavigator
if you are using react navigation 5
you can access by params by
this.props.route.params
if older
this.props.navigation.state.params

Setting params on useEffect and getting the params it on createStackNav in React Navigation 5

I am working on an app where in I need to send or set a particular params of my state in an object.
Here's my object with its respected state:
const [isGlutenFree, setIsGlutenFree] = useState(false);
const [isLactoseFree, setisLactoseFree] = useState(false);
const [isVegan, setIsVegan] = useState(false);
const [isVegetarian, setIsVegetarian] = useState(false);
const saveFilters = () => {
const appliedFilters = {
glutenFree: isGlutenFree,
lactosFree: isLactoseFree,
vegan: isVegan,
isVegetarian: isVegetarian
};
};
These are all sitting on my FiltersScreen.js file. So on my useEffect I tried to use the CommonActions to set the params:
import { CommonActions } from '#react-navigation/native';
useEffect(() => {
props.navigation.dispatch(CommonActions.setParams({ save: saveFilters }));
});
Then inside my Navigator.js file, I tried to receive this and console.log this on the screen to see that it has the data I need:
<FilterNav.Screen
name="Filters"
component={FiltersScreen}
options={({ route }) => ({
title: 'Filters',
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Menu'
iconName='ios-menu'
onPress={() => {
navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Save'
iconName='ios-save'
onPress={() => {
// CONSOLE LOG THE PARAMS SET >>
console.log(route.params.save);
}}
/>
</HeaderButtons>
)
})}
/>
</FilterNav.Navigator>
To be specific on this part:
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Save'
iconName='ios-save'
onPress={() => {
// CONSOLE LOG THE PARAMS SET >>
console.log(route.params.save);
}}
/>
</HeaderButtons>
)
But this doesn't work and doesn't return anything. Also it freezes all of my Switches.
Here's the complete code for FiltersScreen.js:
import React, { useState, useEffect } from 'react';
import { Text, View, StyleSheet, Switch, Platform } from 'react-native';
import { CommonActions } from '#react-navigation/native';
import Colors from '../constants/colors';
const FiltersSwitch = props => {
return(
<View style={styles.filterContainer}>
<Text>{ props.label }</Text>
<Switch
trackColor={{ true: Colors.primaryColor }}
thumbColor={Platform.OS === 'android' ? Colors.primaryColor : ''}
value={props.state}
onValueChange={props.onChange} />
</View>
);
};
const FiltersScreen = props => {
const [isGlutenFree, setIsGlutenFree] = useState(false);
const [isLactoseFree, setisLactoseFree] = useState(false);
const [isVegan, setIsVegan] = useState(false);
const [isVegetarian, setIsVegetarian] = useState(false);
// We will give this a connection with the
// MealsNavigator in order to trigger this
// function via useEffect
const saveFilters = () => {
const appliedFilters = {
glutenFree: isGlutenFree,
lactosFree: isLactoseFree,
vegan: isVegan,
isVegetarian: isVegetarian
};
};
useEffect(() => {
props.navigation.dispatch(CommonActions.setParams({ save: saveFilters }));
});
return (
<View style={styles.screen}>
<Text style={styles.title}>Available Filters / Restrictions</Text>
<FiltersSwitch
label="Glutten Free"
state={isGlutenFree}
onChange={newValue => setIsGlutenFree(newValue)} />
<FiltersSwitch
label="Lactose Free"
state={isLactoseFree}
onChange={newValue => setisLactoseFree(newValue)} />
<FiltersSwitch
label="Vegan"
state={isVegan}
onChange={newValue => setIsVegan(newValue)} />
<FiltersSwitch
label="Vegetarian"
state={isVegetarian}
onChange={newValue => setIsVegetarian(newValue)} />
</View>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
alignItems: 'center'
},
title: {
fontFamily: 'poppins-bold',
fontSize: 22,
margin: 20,
textAlign: 'center'
},
filterContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
width: '80%',
marginVertical: 15
}
});
export default FiltersScreen;
And here's the complete code for the navigation.js file:
const FilterNav = createStackNavigator();
const FiltersNavigator = () => {
return (
<FilterNav.Navigator
mode="modal"
screenOptions={{
headerStyle: {
backgroundColor: Colors.primaryColor,
},
headerTintColor: '#fff',
headerTitleStyle: {
fontSize: 17
}
}}
>
<FilterNav.Screen
name="Filters"
component={FiltersScreen}
options={({ route }) => ({
title: 'Filters',
headerLeft: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Menu'
iconName='ios-menu'
onPress={() => {
navigation.toggleDrawer();
}}
/>
</HeaderButtons>
),
headerRight: () => (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='Save'
iconName='ios-save'
onPress={() => {
console.log(route.params.save);
}}
/>
</HeaderButtons>
)
})}
/>
</FilterNav.Navigator>
);
};
Please help how to solve this been stuck for a couple of time. I am using React Navigation 5.
I guess the "freeze" problem is that your useEffect is executing in every screen render, since it has no dependency array (read Tip: Optimizing Performance by Skipping Effects).
Your code should look something like this:
// useCallback, so it doesn't create a new saveFilters function in every render
const saveFilters = useCallback(() => {
const appliedFilters = {
glutenFree: isGlutenFree,
lactosFree: isLactoseFree,
vegan: isVegan,
isVegetarian: isVegetarian
};
}, [isGlutenFree, isLactoseFree, isVegan, isVegetarian]); // This function depends on those variables.
useEffect(() => {
props.navigation.dispatch(CommonActions.setParams({ save: saveFilters }));
}, [saveFilters]); // It should change the parameterized function if it changes

Categories

Resources