React Navigation and React Context - javascript

In our App we use a tab navigation and a stack navigation for each tab. We want an array of devices where we could add and delete devices. The array should be available on every tab.
This is our provider
import React from 'react'
const DevicesContext = React.createContext('')
export default DevicesContext
This is our app.js
import React, {useState} from 'react';
import uuid from 'react-native-uuid';
import { NavigationContainer } from '#react-navigation/native';
import { createMaterialBottomTabNavigator } from '#react-navigation/material-bottom-tabs';
import { MaterialCommunityIcons } from '#expo/vector-icons';
import { Feather } from '#expo/vector-icons';
import { MaterialIcons } from '#expo/vector-icons';
import HomeStackScreen from "./components/home/HomeStackScreen";
import ConnectStackScreen from "./components/connect/ConnectStackScreen";
import SettingsStackScreen from "./components/settings/SettingsStackScreen";
import DevicesContext from "./components/context/DevicesContext";
const Tab = createMaterialBottomTabNavigator();
const deleteItem = (id) => {
setDevices(prevDevice => {
return prevDevice.filter(device => device.id != id)
})
console.log(devices)
}
const addItem = (device) => {
setDevices(prevDevices => {
return [{id: uuid.v4(), name:device}, ...prevDevices];
})
}
function MyTabs() {
return (
<Tab.Navigator
initialRouteName="Home"
activeColor="#E4E4E4"
inactiveColor="#000000"
shifting={true}
labelStyle={{ fontSize: 12 }}
barStyle={{ backgroundColor: '#8DFFBB' }}
>
<Tab.Screen
name="Devices"
component={ConnectStackScreen}
options={{
tabBarLabel: 'Geräte',
tabBarIcon: ({ color }) => (
<MaterialIcons name="devices" size={24} color={color} />
),
}}
/>
<Tab.Screen
name="Home"
component={HomeStackScreen}
options={{
tabBarLabel: 'Home',
tabBarIcon: ({ color }) => (
<MaterialCommunityIcons name="home" color={color} size={26} />
),
}}
/>
<Tab.Screen
name="Settings"
component={SettingsStackScreen}
options={{
tabBarLabel: 'Einstellungen',
tabBarIcon: ({ color }) => (
<Feather name="settings" size={24} color={color} />
),
}}
/>
</Tab.Navigator>
);
}
export default function App() {
const [devices, setDevices] = useState([
{id: uuid.v4(), name: 'thing 1', ip: 5},
{id: uuid.v4(), name: 'thing 2', ip: 2},
{id: uuid.v4(), name: 'thing 3', ip: 6},
{id: uuid.v4(), name: 'thing 4', ip: 10},
])
return (
<DevicesContext.Provider value={devices}>
<NavigationContainer>
<MyTabs />
</NavigationContainer>
</DevicesContext.Provider>
);
}
this is our connect screen where we can add devices
import React, {useContext, useState} from 'react';
import {Text, View, Button, FlatList, StyleSheet, TouchableOpacity, Image} from 'react-native';
import uuid from 'react-native-uuid';
import ListItem from "../shared/ListItem";
import AddItem from "../shared/AddItem";
import DevicesContext from "../context/DevicesContext";
function ConnectScreen( {navigation}) {
const [devices, setDevices] = useState(useContext(DevicesContext));
const deleteItem = (id) => {
setDevices(prevDevice => {
return prevDevice.filter(device => device.id != id)
})
console.log(devices)
}
const addItem = (device) => {
setDevices(prevDevices => {
return [{id: uuid.v4(), name:device}, ...prevDevices];
})
}
return (
<View style={{padding: 10, flex: 1, justifyContent: 'center'}}>
<View style={styles.AddNormal}>
<AddItem addItem={addItem}></AddItem>
<FlatList style={styles.List} data={devices} renderItem={({item}) => (
<ListItem item={item} deleteItem={deleteItem}></ListItem>
)}/>
</View>
<View style={styles.AddQr}>
<Image source={require('../../img/qr-code-url.png')} style={{ width: 150, height: 150, marginBottom: 10 }} />
<Text style={{ textAlign: 'center', marginBottom: 10 }}>Du kannst außerdem ein Gerät durch das scannen eines Qr-Code hinzufügen</Text>
<TouchableOpacity onPress={() => navigation.navigate('QrCode')}style={styles.btn}>
<Text style={styles.btnText}>Qr-Code scannen</Text>
</TouchableOpacity>
</View>
</View>
);
}
const styles = StyleSheet.create({
List: {
backgroundColor: '#E4E4E4',
},
AddNormal: {
padding: 10, flex: 1,
},
AddQr: {
backgroundColor: '#E4E4E4',
padding: 30,
flex: 1,
marginTop: 20,
marginBottom: 20,
alignItems: 'center'
},
btn: {
backgroundColor: '#8DFFBB',
padding: 9,
margin: 10,
},
btnText: {
color: '#000',
fontSize: 20,
textAlign: 'center',
}
});
export default ConnectScreen;
and this is our main screen
import React, {useState, useContext, useEffect} from 'react';
import {Button, FlatList, SafeAreaView, StatusBar, StyleSheet, Text, TouchableOpacity, View} from "react-native";
import {ServerOnOffSwitch, SendMessage} from "./network";
import DevicesContext from "../context/DevicesContext";
const Item = ({ item, onPress, backgroundColor, textColor }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, backgroundColor]}>
<Text style={[styles.title, textColor]}>{item.name}</Text>
</TouchableOpacity>
);
function HomeScreen (){
const [devices, setDevices] = useState(useContext(DevicesContext));
const deleteItem = (id) => {
setDevices(prevDevice => {
return prevDevice.filter(device => device.id != id)
})
}
const [selectedId, setSelectedId] = useState(null);
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#b5b5b5" : "#ededed";
const color = item.id === selectedId ? 'white' : 'black';
return (
<Item
item={item}
onPress={() => setSelectedId(item.id)}
backgroundColor={{ backgroundColor }}
textColor={{ color }}
/>
);
};
return (
<View style={{padding: 10, flex: 1, justifyContent: 'center'}}>
<View style={{padding: 10, flex: 1}}>
<Text style={styles.DeviceHeader}>Gerät auswählen</Text>
<FlatList
data={devices}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
/>
</View>
<View style={{padding: 10, flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<SendMessage item={selectedId}></SendMessage>
<ServerOnOffSwitch></ServerOnOffSwitch>
</View>
</View>
);
}
const styles = StyleSheet.create({
DeviceHeader: {
fontSize: 22,
paddingBottom: 10,
},
item: {
padding: 10,
backgroundColor: '#f8f8f8',
borderBottomWidth: 1,
borderColor: '#eee',
},
title: {
fontSize: 18,
},
});
export default HomeScreen;
If we add devices in our Connect screen they are getting updated there but not on the homescreen.
Thanks for your help:)

to update context from nested component you must pass the methode setDevices tha will update it.
to pass it do the following steps :
your context shoud be
import React from 'react'
const DevicesContext = React.createContext({
devices: [],
setDevices: () => {}, //methode will update context value
})
export default DevicesContext
App.js should be
//define state
const [devices, setDevices] = React.useState([])
//define constexValue
//we will pass `devices` and also `setDevices` that will update it.
const DevicesContextValue = React.useMemo(() => ({ devices, setDevices}), [devices]);
return (
<DevicesContext.Provider value={DevicesContextValue}>
...
</DevicesContext.Provider>
);
ConnectScreen.js should be
function ConnectScreen(){
const {devices, setDevices} = useContext(DevicesContext);
//call setDevices will update context
....
}
HomeScreen.js should be
function HomeScreen (){
const {devices, setDevices} = useContext(DevicesContext);
//use devices from context in your flatlist and when the context update the result will show in flatlist
....
}

Related

React Context is not updating Data

I have created a React Context API, I have stored information such as first_name, hobbies, DOB etc in it. I have crated a state in the context file and a function that changes the state when called. I am using the context in SignUpForm1.js and SignUpForm2.js, I am trying to update the state of context variables when ever there is a change in the text field, but the data in the context is not updating.
UserData.js (Context)
import React from "react";
import { useState } from "react";
import {Text} from 'react-native';
import { createContext } from "react";
const SignUpContext = React.createContext({});
const UserData = (props)=>{
var state = {
pref_pronoun: "",
DOB: "",
course: "",
first_name: "",
hobby_1: "",
hobby_2: "",
hobby_3: "",
hobby_4: "",
hobby_5: "",
home_city: "",
last_name: "",
nationality: "",
student_email: "",
university: "",
}
const [userDetails , setDetails] = useState(state);
const updateFormData = (field, value) => {
setDetails({ [field]: value });
console.log(state);
};
return (
<SignUpContext.Provider value = {{state, updateFormData}}>
{props.children}
</SignUpContext.Provider>
)
}
export {SignUpContext, UserData} ;
SignUpForm1.js
import {
Image,
Text,
StyleSheet,
View,
StatusBar,
ScrollView,
RefreshControl,
} from "react-native";
import DropDown from "./DropDown";
import Input from "./Input";
import {
KeyboardAvoidingView,
TouchableWithoutFeedback,
Keyboard,
} from "react-native";
import { useCallback, useContext, useState } from "react";
import CustomButton from "./CustomButton";
import { useNavigation } from "#react-navigation/native";
import DateTimePickerModal from "react-native-modal-datetime-picker";
import { Button } from "react-native";
import { SignUpContext, UserData } from "./GlobalUtil/UserData";
const HideKeyboard = ({ children }) => (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
{children}
</TouchableWithoutFeedback>
);
function SignUpForm1() {
const [isDatePickerVisible, setDatePickerVisibility] = useState(false);
const s_context = useContext(SignUpContext);
const showDatePicker = () => {
setDatePickerVisibility(true);
};
const hideDatePicker = () => {
setDatePickerVisibility(false);
};
const navigation = useNavigation();
const NationalityData = ["Football", "Cricket", "Programmming", "Coding"];
const [refreshing, setRefreshing] = useState(false);
const onRefresh = useCallback(() => {
setRefreshing(true);
setTimeout(() => {
setRefreshing(false);
}, 2000);
}, []);
const statusBarHeight = Platform.OS === "ios" ? 50 : StatusBar.currentHeight;
return (
<KeyboardAvoidingView behavior="padding">
<HideKeyboard>
<View
style={{
height: "100%",
width: "100%",
backgroundColor: "#f1be63",
}}
>
<View
style={{ backgroundColor: "#f1be63", height: statusBarHeight }}
>
<StatusBar barStyle="dark-content" />
</View>
<ScrollView
contentContainerStyle={styles.rootContainer}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
/>
}
>
<Image
source={require("../assets/aeroplane.png")}
style={styles.image}
resizeMode="contain"
/>
<Text style={styles.header}>Let's get you set up</Text>
<Text style={styles.lowerHeader}>
(we promise that it won't take long)
</Text>
<View style={[styles.textFieldsContainer]}>
<View style={{ alignItems: "center" }}>
<Input
isLabel={true}
label="Preferred Pronoun"
placeholder="He/Him"
onChangeText={(text) => {
s_context.updateFormData("pref_pronoun", text);
}}
/>
<Input
isLabel={true}
label="First Name"
placeholder="Cameron"
onChangeText={(text) => {
s_context.updateFormData("first_name", text);
}}
/>
<Input
isLabel={true}
label="Last Name"
placeholder="Cox"
onChangeText={(text) => {
s_context.updateFormData("last_name", text);
}}
/>
<View
style={{
backgroundColor: "white",
width: "80%",
borderRadius: 5,
marginTop: 10,
}}
>
<Button
title="Date of Birth"
onPress={showDatePicker}
color="gray"
/>
</View>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
onConfirm={(date) => {
s_context.updateFormData('dob', date);
hideDatePicker();
}}
onCancel={hideDatePicker}
buttonTextColorIOS="white"
pickerContainerStyleIOS={{ backgroundColor: "#D89D35" }}
isDarkModeEnabled
/>
</View>
<View style={{ alignItems: "center" }}>
<DropDown
data={NationalityData}
placeholder="Nationality"
onSelect={(selectedItem, index) => {
s_context.updateFormData("nationality", selectedItem);
}}
/>
<DropDown
data={NationalityData}
placeholder="University"
onSelect={(selectedItem, index) => {
s_context.updateFormData("university", selectedItem);
}}
/>
<DropDown
data={NationalityData}
placeholder="Course"
onSelect={(selectedItem, index) => {
s_context.updateFormData("course", selectedItem);
}}
/>
<DropDown
data={NationalityData}
placeholder="HomeTown City"
onSelect={(selectedItem, index) => {
s_context.updateFormData("homeCity", selectedItem);
}}
/>
<CustomButton
isBorder={true}
title="Next"
onPress={() => {
navigation.navigate("SignUp2");
}}
/>
</View>
</View>
</ScrollView>
</View>
</HideKeyboard>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
rootContainer: {
height: "125%",
justifyContent: "flex-start",
alignItems: "center",
marginTop: 24,
},
textFieldsContainer: {
width: "100%",
flex: 1,
},
image: {
width: "25%",
height: "10%",
marginTop: 24,
},
header: {
color: "white",
fontSize: 26,
fontWeight: "bold",
marginVertical: 6,
},
lowerHeader: {
color: "white",
fontSize: 12,
marginBottom: 24,
},
});
export default SignUpForm1;
SignUpForm2.js
import {
View,
Text,
TouchableWithoutFeedback,
StyleSheet,
StatusBar,
ScrollView,
} from "react-native";
import { KeyboardAvoidingView } from "react-native";
import { Avatar } from "react-native-elements";
import Input from "./Input";
import DropDown from "./DropDown";
import { Keyboard } from "react-native";
import { Platform } from "react-native";
import { useNavigation } from "#react-navigation/native";
import CustomButton from "./CustomButton";
import { useState, useContext } from "react";
import { createNewUser } from "./util/auth";
import { SignUpContext, UserData } from "./GlobalUtil/UserData";
const HideKeyboard = ({ children }) => (
<TouchableWithoutFeedback onPress={() => Keyboard.dismiss()}>
{children}
</TouchableWithoutFeedback>
);
function SignUpForm2() {
const s_context = useContext(SignUpContext);
async function FinishBtnHandler()
{
console.log("Creating New User");
console.log("Email: " + emailText.trim());
console.log("Password: " + passwordText.trim());
await createNewUser(emailText.trim(), passwordText.trim());
}
const navigation = useNavigation();
const hobbiesData = ["Football", "Cricket", "Programmming", "Coding"];
const [emailText, setEmailText] = useState("");
function handleEmailText(newText) {
console.log("Email: " + newText);
setEmailText(newText);
}
const [passwordText, setPasswordText] = useState("");
function handlePasswordText(newText) {
console.log("Password: " + newText);
setPasswordText(newText);
}
function avatarPressHandler() {
console.log("Pressed!");
}
const statusBarHeight = Platform.OS === "ios" ? 50 : StatusBar.currentHeight;
return (
<KeyboardAvoidingView behavior="padding" style={{ flex: 1 }}>
<HideKeyboard>
<ScrollView>
<View
style={{
position: "absolute",
top: 0,
left: 0,
right: 0,
zIndex: 999,
}}
>
<View
style={{
backgroundColor: "#f1be63",
height: statusBarHeight,
}}
>
<StatusBar barStyle="dark-content" />
</View>
</View>
<View style={[styles.rootContainer, { paddingBottom: 48 }]}>
<View style={styles.lowerContainer}>
<Text style={styles.primaryText}>You're almost there!</Text>
<Avatar
rounded
size={170}
containerStyle={{ alignSelf: "center", marginTop: 24 }}
//icon={{ name: "user", type: "font-awesome" }}
overlayContainerStyle={{ backgroundColor: "#f1be63" }}
source={{
uri: "https://cdn.pixabay.com/photo/2019/11/03/20/11/portrait-4599553__340.jpg",
}}
>
<Avatar.Accessory size={20} onPress={avatarPressHandler} />
</Avatar>
<Text
style={[
styles.secondaryText,
{ marginBottom: 8, marginTop: 16 },
]}
>
Express yourself & customize your avatar
</Text>
<Input
isLabel={true}
label="Student Email"
placeholder="cce22rnu#uea.ac.uk"
onChangeText={handleEmailText}
defaultValue={emailText}
/>
<Input
isLabel={true}
label="Create Password"
placeholder="iLoveyoushakila123"
onChangeText={handlePasswordText}
defaultValue={passwordText}
/>
<Input
isLabel={true}
label="Confirm Password"
placeholder="iLoveyoushakila123"
/>
<Text style={styles.secondaryText}>
Now the exciting part - select your top 5 hobbies
</Text>
<View style={{ alignItems: "center", marginTop: 16 }}>
<DropDown
data={hobbiesData}
onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_1", selectedItem);
}}
/>
<DropDown data={hobbiesData} onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_2", selectedItem);
}}/>
<DropDown data={hobbiesData} onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_3", selectedItem);
}}/>
<DropDown data={hobbiesData} onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_4", selectedItem);
}}/>
<DropDown data={hobbiesData} onSelect={(selectedItem, index) => {
s_context.updateFormData("hobby_5", selectedItem);
}}/>
<CustomButton
isBorder={true}
title="Finish"
/*onPress={()=>{navigation.navigate("ConfirmId")}}*/ onPress={
FinishBtnHandler
}
/>
</View>
</View>
</View>
</ScrollView>
</HideKeyboard>
</KeyboardAvoidingView>
);
}
const styles = StyleSheet.create({
rootContainer: {
flex: 1,
justifyContent: "flex-start",
alignItems: "center",
backgroundColor: "#f1be63",
marginTop: 48,
backgroundColor: "#f1be63",
},
lowerContainer: {
flex: 1,
width: "100%",
alignItems: "center",
},
primaryText: {
color: "white",
fontSize: 24,
fontWeight: "bold",
},
secondaryText: {
marginTop: 8,
color: "white",
fontSize: 12,
fontWeight: "bold",
},
});
export default SignUpForm2;
You need to pass your userDetails state variable to the context, not the initial state value
<SignUpContext.Provider value={{ state: userDetails, updateFormData }}>
{props.children}
</SignUpContext.Provider>
Passing the initial state object will not see any updates you make.
Also, you should use the functional updates format to dynamically set properties in your state
setDetails((prev) => ({ ...prev, [field]: value }));
Finally, console logging state is a pointless exercise that often gives unexpected results. Just don't do it
The issue is that you are using state instead of userDetails in the updateFormData function. The state variable is never updated and remains the same initial value. To fix this, you should use setDetails instead of setState in the updateFormData function.
const updateFormData = (field, value) => {
setDetails({ ...userDetails, [field]: value });
console.log(userDetails);
};
Also, you are passing state in the SignUpContext.Provider value. You should pass userDetails instead of state.
return (
<SignUpContext.Provider value={{ userDetails, updateFormData }}>
{props.children}
</SignUpContext.Provider>
);

delete list element using filter method

I am working on todo list in native , i have a function called onDelete but when i click on it , it delete all element in list and after then program crashed.
this is my main file where i have stored value as key , value pair
export default function App() {
const [courseGoal, setCourseGoal] = useState([]);
const [count, setCount] = useState('');
const submitHandler = () => {
setCourseGoal((currGoal) => [
...currGoal,
{ key: Math.random().toString(), value: count },
]);
};
console.log('App', courseGoal)
return (
<View style={styles.container}>
<SearchBar
setCourseGoal={setCourseGoal}
count={count}
setCount={setCount}
submitHandler={submitHandler}
/>
<ListItem courseGoal={courseGoal} setCourseGoal={setCourseGoal} courseGoal={courseGoal}/>
</View>
);
}
this is my list component where i am facing issue, you can see ondelete here.
import React from "react";
import { StyleSheet, Text, TouchableOpacity } from "react-native";
import { FlatList } from "react-native-web";
export default function ListItem(props) {
const onDelete = (goalId) => {
props.setCourseGoal((currGoal) => {
currGoal.filter((goal) => goal.key !== goalId);
console.log("clicked", props.courseGoal[0].key);
});
};
return (
<FlatList
data={props.courseGoal}
keyExtractor={(item, index) => item.key}
renderItem={(itemData) => (
<TouchableOpacity
onPress={onDelete.bind(itemData.item.key)}
activeOpacity={0.2}
>
<Text style={styles.listData}>{itemData.item.value}</Text>
{console.log(itemData.item.key)}
</TouchableOpacity>
)}
/>
);
}
this is my main component where i have my search input
import React from "react";
import { View, Text, StyleSheet, Pressable, TextInput } from "react-native";
export default function SearchBar(props) {
const onInputHandler = (value) => {
props.setCount(value);
};
return (
<View style={styles.searchBox}>
<Pressable style={styles.submitBtn} title="Click Me !">
<Text>☀️</Text>
</Pressable>
<TextInput
style={styles.searchInput}
placeholder="Enter Goal"
onChangeText={onInputHandler}
/>
<Pressable
style={styles.submitBtn}
title="Click Me !"
onPress={props.submitHandler}
>
<Text>🔥🔥</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
searchBox: {
flexDirection: "row",
justifyContent: "space-around",
},
searchInput: {
width: "90%",
textAlign: "center",
padding: 10,
// color: 'white',
borderRadius: 10,
borderWidth: 1,
marginHorizontal: 5,
},
submitBtn: {
color: "black",
justifyContent: "space-around",
padding: 10,
borderRadius: 10,
borderWidth: 1,
},
});
You have to return the filtered array from your onDelete function
const onDelete = (goalId) => {
props.setCourseGoal((currGoal) => {
return currGoal.filter((goal) => goal.key !== goalId);
});
};

TypeError: undefined is not an object (evaluating'_order.default.addOrder')

I am trying to call the addOrder function from CartScreen to function located in my action folder.
the thing is whenever I pressed the OrderNow Button, the addOrder function should be triggered. But, I'm getting error like this.
CartScreen.js
import React from 'react';
import { View, Text, FlatList, Button, StyleSheet } from 'react-native';
import { useSelector, useDispatch } from 'react-redux';
import * as cartAction from '../../store/actions/cart';
import ordersAction from '../../store/actions/order';
import Colors from '../../constants/Colors';
import CartItem from '../../components/shop/CartItem';
const CartScreen = props => {
const dispatch = useDispatch();
const cartTotalAmount = useSelector(state => state.cart.totalAmount);
const cartItems = useSelector(state => {
const transformedCartItems = [];
for (const key in state.cart.items) {
transformedCartItems.push({
productId: key,
productTitle: state.cart.items[key].productTitle,
productPrice: state.cart.items[key].productPrice,
quantity: state.cart.items[key].quantity,
sum: state.cart.items[key].sum,
});
}
return transformedCartItems.sort((a, b) =>
a.productId > b.productId ? 1 : -1,
);
});
console.log('CATRITEM', cartItems);
return (
<View style={styles.screen}>
<View style={styles.summary}>
<Text style={styles.summaryText}>
Total:{' '}
<Text style={styles.amount}>${cartTotalAmount.toFixed(2)}</Text>
</Text>
<Button
color={'green'}
title="Order Now"
disabled={cartItems.length === 0}
onPress={() => {
dispatch(ordersAction.addOrder(cartItems, cartTotalAmount));
}}
/>
</View>
<FlatList
data={cartItems}
keyExtractor={item => item.productId}
renderItem={itemData => (
<CartItem
quantity={itemData.item.quantity}
title={itemData.item.productTitle}
amount={itemData.item.sum}
onRemove={() => {
dispatch(cartAction.removeFromCart(itemData.item.productId));
}}
/>
)}
/>
</View>
);
};
CartScreen.navigationOptions = {
headerTitle: 'Your Cart',
};
const styles = StyleSheet.create({
screen: {
margin: 20,
},
summary: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 20,
padding: 10,
shadowColor: 'black',
shadowOpacity: 0.26,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 8,
elevation: 5,
borderRadius: 10,
backgroundColor: 'white',
},
summaryText: {
fontSize: 18,
},
amount: {
color: Colors.primary,
},
});
export default CartScreen;
order.js (in action Folder)
export const ADD_ORDER = 'ADD_ORDER';
export const addOrder = (cartItems, totalAmount) => {
return {
type: ADD_ORDER,
orderData: {
items: cartItems,
amount: totalAmount,
},
};
};
Use the import differently and call the function as:
import { addOrder } from '../../store/actions/order';
Then call as the following:
dispatch(addOrder(cartItems, cartTotalAmount));
your import is wrong . you imported orderAction like
import ordersAction from '../../store/actions/order';
what you should do is
import * as ordersAction from '../../store/actions/order';
things should work fine after

Unable to navigate to next screen in React Native

I have created a list of posts and want to pass details of one specfic post to another screen. I want to be able to click on the post and be directed to the post details screen. This is the PostList.js file:
import React, {Component} from 'react';
import {
FlatList,
StyleSheet,
View,
Text,
Modal,
TouchableOpacity,
} from 'react-native';
import Post from './Post';
import Firebase from 'firebase';
import 'firebase/database';
import {firebaseConfig} from './configFirebase';
import PostDetails from './stack/PostDetails';
export default class Posts extends Component {
constructor(props) {
super(props);
!Firebase.apps.length
? Firebase.initializeApp(firebaseConfig.firebase)
: Firebase.app();
this.state = {
postList: [],
navigation: this.props.navigation,
};
}
state = {
loading: false,
currentPost: null,
};
componentDidMount() {
this.getPostData();
}
getPostData = () => {
const ref = Firebase.database().ref('/posts');
ref.on('value', snapshot => {
console.log('DATA RETRIEVED');
const postsObject = snapshot.val();
if (!postsObject) {
return console.warn('No data from firebase');
}
const postsArray = Object.values(postsObject);
this.setState({postList: postsArray});
});
};
render() {
return (
<View style={styles.container}>
<FlatList
keyExtractor={post => post.heading}
data={this.state.postList}
renderItem={({item: post}) => (
<Post
key={post.heading}
heading={post.heading}
description={post.description}
location={post.location}
onPress={() => this.props.navigation.push('PostDetails', {post})}
/>
)}
/>
</View>
);
}
}
export const styles = StyleSheet.create({
container: {
borderWidth: 2,
borderRadius: 5,
backgroundColor: '#2bb76e',
flex: 1,
},
txtInput: {
flex: 1,
margin: 5,
padding: 5,
borderWidth: 2,
fontSize: 20,
borderRadius: 5,
backgroundColor: 'snow',
},
});
I've tried navigation.navigate() and navigation.push() and neither work.
This is the PostDetails Screen I want to navigate to and pass the post info to:
import React from 'react';
import {Text, View} from 'react-native';
export default ({route}) => {
const postInfo = route.params.post;
return (
<View>
<Text>{JSON.stringify(postInfo, null, 2)}</Text>
<Text>{postInfo.heading}</Text>
</View>
);
};
This is my HomeStack file where the screens are kept:
import React from 'react';
import {NavigationContainer} from '#react-navigation/native';
import {createStackNavigator} from '#react-navigation/stack';
import Posts from '../PostList';
import AddForm from '../AddForm';
import PostDetails from './PostDetails';
const HomeStack = createStackNavigator();
const HomeStackScreen = () => (
<HomeStack.Navigator>
<HomeStack.Screen
name="PostList"
component={Posts}
options={{headerTitle: 'big APPetite'}}
/>
<HomeStack.Screen
name="PostDetails"
component={PostDetails}
options={({route}) => ({heading: route.params.post.heading})}
/>
<HomeStack.Screen name="NewPost" component={AddForm} />
</HomeStack.Navigator>
);
export default HomeStackScreen;
Post.js:
import React, {Component} from 'react';
import {
Image,
Text,
StyleSheet,
View,
TextInput,
FlatList,
TouchableOpacity,
} from 'react-native';
import FavouriteButton from './buttons/FavouriteButton';
import chickenClub from './images/chickenSandwich.jpg';
const Post = ({heading, description, location, username}) => (
<TouchableOpacity style={postStyle.container}>
<View style={(postStyle.container, {alignItems: 'flex-start'})}>
<View style={postStyle.padding}>
<Image style={postStyle.image} source={chickenClub} />
<View style={{backgroundColor: (255, 255, 255, 45), borderRadius: 6}}>
<Text style={postStyle.text}>{heading}</Text>
<Text style={postStyle.text}>{location}</Text>
<Text style={postStyle.text}>{username}*username*</Text>
</View>
</View>
<View
style={{
alignSelf: 'flex-end',
flexDirection: 'column',
backgroundColor: '#2bb76e',
}}>
<Text style={postStyle.paragraph}>{description}</Text>
<View style={{justifyContent: 'flex-start', alignItems: 'flex-end'}}>
<FavouriteButton />
</View>
</View>
</View>
</TouchableOpacity>
);
const postStyle = StyleSheet.create({
container: {
borderWidth: 2,
borderRadius: 5,
backgroundColor: '#2bb76e',
flex: 1,
},
padding: {
padding: 10,
},
heading: {
backgroundColor: (255, 250, 250, 50),
flexDirection: 'column',
},
paragraph: {
alignSelf: 'flex-end',
fontSize: 20,
},
username: {},
image: {
flexDirection: 'row',
height: 150,
width: 150,
},
text: {
fontSize: 25,
padding: 5,
},
});
export default Post;
You are passing onPress to your Post component, however you are not applying that prop to the TouchableOpacity.
Modifying the Post component to include:
const Post = ({heading, description, location, username, onPress}) => (
<TouchableOpacity style={postStyle.container} onPress={onPress}>

Undefined is not an object (evaluating 'this.props.navigation.navigate') _onViewApp ln 17

I did find this question in a few other places, but I'm still unable to resolve the issue with any of the help given. I got most of the code so far from this article -
https://medium.com/#austinhale/building-a-mobile-app-in-10-days-with-react-native-c2a7a524c6b4
Some of the APIs were outdated, but most of them I was able to replace with the new version - One of the APIs i changed that might be notable in regards to this question is stackNavigator -> createStackNavigator
My code is as follows:
Apps.js
import React, { Component } from 'react';
import {
FlatList,
StatusBar,
StyleSheet,
Text,
View
} from 'react-native';
import AppItem from './AppItem';
export default class Apps extends Component {
constructor(props) {
super(props);
this.state = {
apps: [
{
id: 1,
title: 'Good Business',
description: 'Make millions investing in mindtree minds',
thumbnail: 'https://img12.androidappsapk.co/300/f/5/1/com.nse.bse.sharemarketapp.png'
},
{
id: 2,
title: 'Troll WhatsApp SOS ONLY',
description: 'Have a laugh by reading all the comments in the group chat made by your coworkers',
thumbnail: 'http://icons.iconarchive.com/icons/dtafalonso/android-l/256/WhatsApp-icon.png'
}
]
}
}
_renderItem = ({ item }) => (
<AppItem
id={item.id}
title={item.title}
description={item.description}
thumbnail={item.thumbnail}
/>
);
_keyExtractor = (item, index) => item.id.toString();
render() {
return (
<View style={styles.container}>
<StatusBar
barStyle="light-content"
/>
<FlatList
data={this.state.apps}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
}
});
router.js
import React, { Component } from 'react';
import { Dimensions, Platform } from 'react-native';
import { createStackNavigator, createBottomTabNavigator } from 'react-navigation';
import { Icon } from 'react-native-elements';
import Apps from './screens/Apps';
import Gallery from './screens/Gallery';
import Garage from './screens/Garage';
import News from './screens/News';
import Support from './screens/Support';
import ViewApp from './screens/ViewApp';
let screen = Dimensions.get('window');
export const Tabs = createBottomTabNavigator({
'Apps': {
screen: Apps,
navigationOptions: {
tabBarLabel: 'Apps',
tabBarIcon: ({ tintColor }) => <Icon raised name="ios-apps-outline" type="ionicon" size={28} color={tintColor} />
},
},
'News': {
screen: News,
navigationOptions: {
tabBarLabel: 'News',
tabBarIcon: ({ tintColor }) => <Icon raised name="newspaper-o" type="font-awesome" size={28} color={tintColor} />
},
},
'Garage': {
screen: Garage,
navigationOptions: {
tabBarLabel: 'Garage',
tabBarIcon: ({ tintColor }) => <Icon raised name="garage" type="material-community" size={28} color={tintColor} />
},
},
'Gallery': {
screen: Gallery,
navigationOptions: {
tabBarLabel: 'Gallery',
tabBarIcon: ({ tintColor }) => <Icon raised name="picture" type="simple-line-icon" size={28} color={tintColor} />
},
},
'Support': {
screen: Support,
navigationOptions: {
tabBarLabel: 'Support',
tabBarIcon: ({ tintColor }) => <Icon raised name="ios-person-outline" type="ionicon" size={28} color={tintColor} />
},
},
});
export const AppsStack = createStackNavigator({
Apps: {
screen: Apps,
navigationOptions: ({ navigation }) => ({
header: null,
}),
},
ViewApp: {
screen: ViewApp,
navigationOptions: ({ navigation }) => ({
header: null,
tabBarVisible: false,
gesturesEnabled: false
}),
},
});
export const createRootNavigator = () => {
return createStackNavigator(
{
AppsStack: {
screen: AppsStack,
navigationOptions: {
gesturesEnabled: false
}
},
Tabs: {
screen: Tabs,
navigationOptions: {
gesturesEnabled: false
}
}
},
{
headerMode: "none",
mode: "modal"
}
);
};
AppItem.js
import React, { Component } from 'react';
import {
StyleSheet,
TouchableOpacity,
Text,
Image,
View
} from 'react-native';
import { Icon } from 'react-native-elements';
import { ViewApp } from './ViewApp';
export default class AppItem extends Component {
_onViewApp = () => {
let id = this.props.id;
this.props.navigation.navigate('ViewApp', { id: id })
}
render() {
return (
<TouchableOpacity onPress={this._onViewApp}>
<View style={styles.rowContainer}>
<Image source={{ uri: this.props.thumbnail }}
style={styles.thumbnail}
resizeMode="contain" />
<View style={styles.rowText}>
<Text style={styles.title} numberOfLines={2} ellipsizeMode={'tail'}>
{this.props.title}
</Text>
<Text style={styles.description} numberOfLines={2} ellipsizeMode={'tail'}>
{this.props.description}
</Text>
</View>
</View>
</TouchableOpacity>
);
}
}
const styles = StyleSheet.create({
rowContainer: {
flexDirection: 'row',
backgroundColor: '#FFF',
height: 100,
padding: 10,
marginRight: 10,
marginLeft: 10,
marginTop: 15,
borderRadius: 4,
shadowOffset: { width: 1, height: 1, },
shadowColor: '#CCC',
shadowOpacity: 1.0,
shadowRadius: 1
},
title: {
paddingLeft: 10,
paddingTop: 5,
fontSize: 16,
fontWeight: 'bold',
color: '#777'
},
description: {
paddingLeft: 10,
marginTop: 5,
fontSize: 14,
color: '#777'
},
thumbnail: {
flex: 1,
height: undefined,
width: undefined
},
rowText: {
flex: 4,
flexDirection: 'column'
}
});
You can change your onPress functionality in AppItem.js like below. Use arrow function inside your onPress onPress={() => this._onViewApp()}. Because Arrow function does not create the context "this". It refers to the context of the component that it was wrapped in. For lexical this refer this Arrow function vs function declaration / expressions: Are they equivalent / exchangeable?
import { Icon } from 'react-native-elements';
import { ViewApp } from './ViewApp';
export default class AppItem extends Component {
_onViewApp(){
let id = this.props.id;
this.props.navigation.navigate('ViewApp', { id: id })
}
render() {
return (
<TouchableOpacity onPress={() => this._onViewApp()}>
<View style={styles.rowContainer}>
<Image source={{ uri: this.props.thumbnail }}
style={styles.thumbnail}
resizeMode="contain" />
<View style={styles.rowText}>
<Text style={styles.title} numberOfLines={2} ellipsizeMode={'tail'}>
{this.props.title}
</Text>
<Text style={styles.description} numberOfLines={2} ellipsizeMode={'tail'}>
{this.props.description}
</Text>
</View>
</View>
</TouchableOpacity>
);
}
}
I wanted to follow up for everyone who's having the same issue. I just included withNavigation from 'react-native-navigation'; and exported at the end of the component like so export default withNavigation(AppItem);.

Categories

Resources