Struggling to update button style on press - javascript

In our code we want the individual button to update style on press and to be able to basically have it update state to where we know it is pressed and can send that it was taken to our database when the submit button is pressed. We have the submit button working but we can't seem to update the seat buttons when we press them to change their color. Instead, we have to wait and reload the page before it changes anything. Here is the code below.
export const OrderScreen = ({ navigation }) => {
const [counter, setCounter] = useState(0);
LogBox.ignoreLogs(['Setting a timer']);
const [order, setOrder] = useState('Unknown');
const COL = 5;
const [data, setData] = useState([]);
const [seats, setSeats] = useState([]);
const updateSeat = async (taken,rowNum,seat) => {
const DocRef = doc(db, "Theaters/Seating/Theater1", rowNum);
await updateDoc(DocRef, {
[seat] : taken
});
};
var ListofOrders = [];
function recordOrder(name,qty){
console.log(name, qty);
ListofOrders = ListofOrders.filter(function( element ) {
return element !== undefined;
});
if(ListofOrders.length != 0){
for (let i = 0; i < ListofOrders.length; i++ ){
//console.log(ListofOrders[i][0]);
if(ListofOrders[i][0] == name){
delete ListofOrders[i];
}
}
}
var array = [name,qty];
ListofOrders.push(array);
//console.log(ListofOrders);
};
const SubmitOrder = async() =>{
for (let i = 0; i < ListofOrders.length; i++ ){
const q = query(collection(db, 'Inventory'), where("name" , "==" , ListofOrders[i][0]));
//console.log(ListofOrders[i][0]);
const querySnapshot = getDocs(q);
querySnapshot.forEach((doc) => {
console.log(doc.id);
docid = doc.id;
});
const dbRef = doc(db, 'Inventory', docid);
updateDoc(dbRef, {
qty: ListofOrders[i][1]
});
}
console.log("successful");
};
function seatTaken(position){
switch(position){
case true: return(styles.seatButton); break;
case false: return(styles.seatButtonTaken); break;
}
};
//function handleClick (){
// setState('buttontaken')
// }
useEffect(() => {
const getInventory = async () => {
const inventorySnapshot = await getDocs(collection(db, "Inventory"));
const inventoryList = inventorySnapshot.docs.map((doc) => doc.data());
setData(inventoryList);
// console.log(inventoryList);
};
getInventory();
const getSeating = async () => {
const seatSnapshot = await getDocs(collection(db, "Theaters/Seating/Theater1"));
const seatList = seatSnapshot.docs.map((doc) => doc.data());
setSeats(seatList);
//console.log(seatList);
};
getSeating();
}, [])
return (
<View style={styles.container}>
<ScrollView>
<Text style={styles.screenTitle}>New Order</Text>
{/* Theater Selection */}
<Text style ={styles.text}> Ticket Order</Text>
<Picker
selectedValue={order}
onValueChange={(value, index) => setOrder(value)}
mode="dropdown" // Android only
style={styles.picker}
>
<Picker.Item label="Please select your Order" value="Unknown" />
<Picker.Item label="Theater 1 5PM" value="Theater 1" />
<Picker.Item label="Theater 2 5PM" value="Theater 2" />
</Picker>
{/* Seat Animation */}
{seats.map((seats,index1,index2,index3) =>(
<React.Fragment>
<View style = {styles.containerseats}>
<Text style = {styles.itemText}> Row {index1 + 1} </Text>
<Button key = {seats} style = {seatTaken(seats.A)} onPress = {() => updateSeat(false, 'Row' + (index1 + 1),'A')}/>
<Button key = {index1} style = {seatTaken(seats.B)} onPress = {() => updateSeat(false, 'Row' + (index1 + 1),'B')}/>
<Button key = {index2} style = {seatTaken(seats.C)} onPress = {() => updateSeat(false, 'Row' + (index1 + 1),'C')}/>
<Button key = {index3} style = {seatTaken(seats.D)} onPress = {() => updateSeat(false, 'Row' + (index1 + 1),'D')} />
</View>
</React.Fragment>
))}
{/* Food order */}
{data.map((data,qty) =>(
<React.Fragment>
<View style = {styles.parent}>
<View style = {styles.block}>
<Text key = {data} style = {styles.itemText}> {data.name}</Text>
</View>
<View style = {styles.block} >
<Counter start = {parseInt(0)} max = {parseInt(data.qty)} onChange={(len, type) => {
console.log(len, type);
qty = len;
recordOrder(data.name,qty);
}} />
</View>
</View>
</React.Fragment>
))}
{/* Buttons */}
<Button style={styles.buttonsubmit} onPress = {() => SubmitOrder()}>
<Text style={styles.buttonText}>Submit Order</Text>
</Button>
<Button style={styles.borderlessButtonContainer} borderless
title={'Cancel Order'}
onPress = {() => navigation.navigate('CustomerHomeScreen')} />
</ScrollView>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'black',
paddingHorizontal: 12,
alignItems: 'center'
},
screenTitle: {
fontSize: 18,
fontWeight: '700',
color: Colors.orange,
paddingTop: 10,
textAlign: 'center',
},
text: {
fontSize: 18,
fontWeight: '700',
color: Colors.orange,
paddingTop: 10,
textAlign: 'center',
},
button: {
width: '10%',
height: 18,
marginTop: 8,
alignItems: 'center',
backgroundColor: Colors.orange,
padding: 10,
borderRadius: 8,
marginRight:8
},
buttonsubmit: {
width: '50%',
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
marginTop: 8,
backgroundColor: Colors.orange,
padding: 10,
borderRadius: 8,
},
buttontaken: {
width: '10%',
height: 18,
marginTop: 8,
backgroundColor: Colors.black,
borderColor: Colors.white,
padding: 10,
borderWidth:2,
borderRadius:8,
marginRight:8,
alignItems: 'center',
justifyContent: 'center',
},
buttonText: {
fontSize: 20,
color: Colors.white,
fontWeight: '700',
},
borderlessButtonContainer: {
marginTop: 16,
alignItems: 'center',
},
picker: {
marginVertical: 30,
width: 300,
padding: 10,
borderWidth: 5,
color: Colors.black,
backgroundColor: 'white',
},
containerseats: {
flexDirection: 'row',
flex: 1,
alignContent: 'space-around',
padding: 5
},
seatButton: {
width: '10%',
height: 18,
marginTop: 8,
backgroundColor: Colors.orange,
padding: 10,
borderRadius: 8,
marginRight:8
},
seatButtonTaken: {
width: '10%',
height: 18,
marginTop: 8,
backgroundColor: Colors.black,
borderColor: Colors.white,
padding: 9,
borderWidth:2,
borderRadius:8,
marginRight:8,
alignItems: 'center',
justifyContent: 'flex-end',
},
itemText: {
fontSize: 20,
fontWeight: '500',
color: '#ff9361',
},
parent: {
flexDirection: "row",
},
block: {
flex: 3,
margin: 6,
},
});
It is pulling from a firebase database and looks like the image below. I would like the button I press to immediately update the style of that button to seatbuttontaken style instead of seatbutton. Please let me know if there's any other helpful information I can provide.

Related

React Native - Flatlist not scrolling in custom dropdown

I have Flatlist that doesn't scroll.
I can't find the problem why its not scrolling.
When I'm trying to scroll, It does nothing. It doesn't give me to scroll past the Produce item.
It looks like this (I have more items after Produce):
This is my code:
const Dropdown: FC<Props> = ({ label, data, onSelect, current, style }) => {
const [dropdownTop, setDropdownTop] = useState(0);
const [visible, setVisible] = useState(false);
const [selected, setSelected] = useState(current);
const DropdownButton = useRef();
const toggleDropdown = (): void => {
visible ? setVisible(false) : openDropdown();
};
const openDropdown = (): void => {
DropdownButton.current.measure((_fx, _fy, _w, h, _px, _py) => {
setDropdownTop(_py + h - 10);
});
setVisible(true);
};
const renderDropdown = (): ReactElement<any, any> => {
return (
<Modal visible={visible} transparent animationType='none'>
<TouchableOpacity
style={styles.overlay}
onPress={() => setVisible(false)}>
<View style={[styles.dropdown, { top: dropdownTop }]}>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item, index) => index.toString()}
contentContainerStyle={{
flexGrow: 1,
flex: 1,
}}
/>
</View>
</TouchableOpacity>
</Modal>
)
};
const renderItem = ({ item }): ReactElement<any, any> => (
<TouchableOpacity style={styles.item} onPress={() => onItemPress(item)}>
<Text style={{ color: "#616161" }}>{item.label}</Text>
</TouchableOpacity>
)
const onItemPress = (item): void => {
setSelected(item);
onSelect(item);
setVisible(false);
};
return (
<TouchableOpacity
ref={DropdownButton}
style={[styles.button, style]}
activeOpacity={0.6}
onPress={toggleDropdown}>
{renderDropdown()}
<Text style={styles.buttonSecondText}>{label}</Text>
<Text style={styles.buttonText}>{(selected && selected.label) || null}</Text>
</TouchableOpacity>
)
}
export default Dropdown
const styles = StyleSheet.create({
item: {
paddingHorizontal: 10,
paddingVertical: 10,
// borderBottomWidth: 1,
borderWidth: 1,
},
button: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#FFF',
elevation: 10,
width: '100%',
paddingHorizontal: 13,
paddingVertical: 13,
borderRadius: 10,
zIndex: 1,
},
buttonSecondText: {
flex: 2,
color: '#000',
textAlign: 'left',
},
buttonText: {
flex: 2,
textAlign: 'right',
color: '#000',
},
dropdown: {
position: 'absolute',
backgroundColor: '#FFF',
paddingHorizontal: 20,
width: '100%',
shadowColor: '#000000',
shadowRadius: 4,
shadowOffset: { height: 4, width: 0 },
shadowOpacity: 0.5,
},
});
I can't understand where is my problem and why its not scrolling.
Maybe its because its inside a Modal? I'm first time trying to use Modal in React Native

TypeError: undefined is not an object (evaluating '_app.default.initializeApp')

i got this problem when i try to save all date in firebase realTime Database pressing submit.
ERROR TypeError: undefined is not an object (evaluating '_app.default.initializeApp')
import firebase from 'firebase';
import React, { useState } from 'react';
import { View, Text, Modal, TouchableOpacity, FlatList, StyleSheet, DatePickerIOS, Image } from 'react-native';
import { Picker } from '#react-native-picker/picker';
const App = () => {
const [chosenDate, setChosenDate] = useState(new Date());
const [selectedValue, setSelectedValue] = useState('Alege Specializarea');
const [selectedDoctor, setSelectedDoctor] = useState('');
const [modalVisible, setModalVisible] = useState(false);
const [showPicker, setShowPicker] = useState(false);
const [showButtons, setShowButtons] = useState(false);
const options = ['Alege Specializarea', 'Cardiologie', 'Dermatologie', 'Oftalmologie', 'Analize Medicale'];
const optionMapping = {
'Alege Specializarea': [],
'Cardiologie': ['Dr. Alex', 'Dr. Balut'],
'Dermatologie': ['Dr. Chloe', 'Dr. David'],
'Oftalmologie': ['Dr. Emily', 'Dr. Frank'],
'Analize Medicale': ['Dr. Grace', 'Dr. Henry'],
};
const firebaseConfig = {
apiKey: "AIzaSyBhyYCXEaUlXqr83GtcbAfV2fiFooGzm2k",
authDomain: "first-app-7901e.firebaseapp.com",
databaseURL: "https://first-app-7901e-default-rtdb.europe-west1.firebasedatabase.app",
projectId: "first-app-7901e",
storageBucket: "first-app-7901e.appspot.com",
messagingSenderId: "876718439254",
appId: "1:876718439254:web:afe7784b89f066210334f7",
};
firebase.initializeApp(firebaseConfig);
const writeAppointmentData = (appointment) => {
firebase
.database()
.ref('appointments/' + appointment.id)
.set(appointment);
};
// Read data from the database
const readAppointmentData = (appointmentId) => {
return firebase
.database()
.ref('appointments/' + appointmentId)
.once('value')
.then((snapshot) => {
return snapshot.val();
});
};
const handleSubmit = () => {
const appointment = {
date: chosenDate,
specialization: selectedValue,
doctor: selectedDoctor,
};
writeAppointmentData(appointment);
setModalVisible(true);
};
const handleRead = () => {
readAppointmentData(appointmentId).then((appointment) => {
setAppointment(appointment);
});
};
return (
<>
<View style={styles.container}>
<Image source={require('./book.png')} style={styles.image} />
</View>
<View style={styles.container}>
<DatePickerIOS
date={chosenDate}
onDateChange={setChosenDate}
minuteInterval={30}
/>
<TouchableOpacity
style={styles.selectionButton}
onPress={() => setShowPicker(!showPicker)}
>
<Text style={styles.selectionButtonText}>{selectedValue}</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.submitButton}
onPress={handleSubmit}
>
<Text style={styles.submitButtonText}>Submit</Text>
</TouchableOpacity>
{showPicker && (
<Picker
selectedValue={selectedValue}
style={styles.picker}
onValueChange={(itemValue) => {
setSelectedValue(itemValue);
setShowButtons(itemValue !== 'Alege Specializarea');
}}
>
{options.map((option) => (
<Picker.Item
label={option}
value={option}
key={option}
/>
))}
</Picker>
)}
{showButtons && optionMapping[selectedValue].length > 0 && (
<View style={styles.buttonContainer}>
{optionMapping[selectedValue].map((buttonName) => (
<TouchableOpacity
style={[
styles.button,
buttonName === selectedDoctor && {
backgroundColor: '#24b6d4',
},
]}
key={buttonName}
onPress={() => setSelectedDoctor(buttonName)}
>
<Text
style={[
styles.buttonText,
buttonName === selectedDoctor && { color: 'white' },
]}
>
{buttonName}
</Text>
</TouchableOpacity>
))}
</View>
)}
</View>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
>
<View style={styles.centeredView}>
<View style={styles.modalView}>
<Text style={styles.modalText}>
Your appointment has been scheduled for{' '}
{chosenDate.toString()} with {selectedDoctor}
</Text>
<TouchableOpacity
style={styles.modalButton}
onPress={() => setModalVisible(false)}
>
<Text style={styles.modalButtonText}>Close</Text>
</TouchableOpacity>
</View>
</View>
</Modal>
</>
);
};
const styles = StyleSheet.create({
container: {
top: -160,
flex: 1,
width: 350,
justifyContent: 'center',
},
image: {
alignSelf: 'center',
},
selectionButton: {
backgroundColor: 'lightgray',
padding: 10,
borderRadius: 10,
left: 20,
},
selectionButtonText: {
fontSize: 18,
},
picker: {
width: '100%',
},
buttonContainer: {
flexDirection: 'row',
justifyContent: 'center',
},
button: {
backgroundColor: '#fff',
borderWidth: 1,
borderColor: '#24b6d4',
borderRadius: 10,
padding: 10,
margin: 10,
width: 120,
},
buttonText: {
color: '#24b6d4',
fontSize: 18,
textAlign: 'center',
},
submitButton: {
backgroundColor: '#24b6d4',
borderRadius: 10,
padding: 10,
margin: 10,
},
submitButtonText: {
color: 'white',
fontSize: 18,
textAlign: 'center',
},
centeredView: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginTop: 22,
},
modalView: {
margin: 20,
backgroundColor: 'white',
borderRadius: 20,
padding: 35,
alignItems: 'center',
shadowColor: '#000',
shadowOffset: {
width: 0,
height: 2,
},
shadowOpacity: 0.25,
shadowRadius: 3.84,
elevation: 5,
},
modalText: {
marginBottom: 15,
textAlign: 'center',
},
modalButton: {
backgroundColor: '#24b6d4',
borderRadius: 10,
padding: 10,
margin: 10,
},
modalButtonText: {
color: 'white',
fontSize: 18,
textAlign: 'center',
},
});
export default App;
I use ExpoGO to "export" the project.
I tried many changes at firebase like: import firebase from 'firebase'; and all that changes for import firebase,none of that works but i tried with Firebase JS SDK and still dosent work.

React Native Using Decimals

Hi I was working on a project, and I am on a part where I am adding up the total cost of orders in the order summary page. I have used
`
let totalPrice = Object.keys(cartItem).map((menuKey) => (
totalPrice =+ cartItem[menuKey].cost))
`
to sum up the cost and display totalPrice
I understand that it might be in String right now, although I am not sure why, because I have passed the param as a number (4.29)
Right now I have 3 items in my cart, all 4.29 for now.
As expected from a string's behavior, it is being displayed as 4.294.294.29.
Problem number 1) parseDouble not working.
Problem number 2) I have no idea how to use the toFixed(2) method on my code.
Any Idea on how I can implement the total cost method?
Whole File :
`
import {
View,
StyleSheet,
ScrollView,
Alert,
ActivityIndicator,
TouchableOpacity,
Button,
Text
} from "react-native";
import AsyncStorage from "#react-native-async-storage/async-storage";
import { useFocusEffect } from "#react-navigation/native";
import ItemCard from "../components/ItemCard";
import { useState, useCallback } from "react";
import { Swipeable } from "react-native-gesture-handler";
import { Ionicons } from "#expo/vector-icons";
export default function CartScreen({ navigation }) {
const [loading, setLoading] = useState(true);
const [cartItem, setcartItem] = useState({});
const [prevOpenedRow, setPrevOpenedRow] = useState();
const [selectedMenu, setselectedMenu] = useState({});
const CART_KEY = "#carts_Key";
const saveCart = async (menuObj) => {
try {
const jsonValue = JSON.stringify(menuObj);
await AsyncStorage.setItem(CART_KEY, jsonValue);
} catch (e) {
alert(`${title}: ${e}`);
}
};
const deleteCart = async (menuKey) => {
const newItem = { ...cartItem };
delete newItem[menuKey];
setcartItem(newItem);
await saveCart(newItem);
};
const alertBeforeDelete = (menuKeyToDelete) => {
Alert.alert(
"Remove from Cart",
`Removing "${cartItem[menuKeyToDelete].title}"`,
[
{
text: "Cancel",
},
{
text: "Remove",
onPress: () => deleteCart(menuKeyToDelete),
style: "destructive",
},
]
);
};
const clearCart = async () => {
const emptyCart = {};
setcartItem(emptyCart);
await saveCart(emptyCart);
};
const alertBeforeClear = () => {
Alert.alert(
"Clearing All Items at Cart",
"Clearing Cart Now. Are you sure?",
[
{
text: "Cancel",
},
{
text: "Yes, Clear",
onPress: () => clearCart(),
style: "destructive",
},
]
);
};
console.log(cartItem)
let totalPrice = Object.keys(cartItem).map((menuKey) => (
totalPrice =+ cartItem[menuKey].cost))
// Swipeable code modified;
const renderRightActions = (progress, dragX, alertBeforeDelete) => {
return (
<View
style={{
margin: 0,
alignContent: "center",
justifyContent: "center",
width: 70,
}}
>
<TouchableOpacity
style={styles.deleteButton}
onPress={alertBeforeDelete}
>
<Ionicons name="trash" size={40} color="#fff" />
</TouchableOpacity>
</View>
);
};
const closeRow = (menuKey) => {
if (prevOpenedRow && prevOpenedRow !== selectedMenu[menuKey]) {
prevOpenedRow.close();
}
setPrevOpenedRow(selectedMenu[menuKey]);
};
useFocusEffect(
useCallback(() => {
const getCart = async () => {
try {
const jsonValue = await AsyncStorage.getItem(CART_KEY);
setcartItem(jsonValue != null ? JSON.parse(jsonValue) : {});
} catch (e) {
alert(`${e}`);
}
};
getCart();
setLoading(false);
return () => {
};
}, [])
);
return loading ? (
<View style={styles.loadingPage}>
<ActivityIndicator size="large" color="#ffffff" />
</View>
) : (
<>
<Text style={styles.titleText}>Your Current Orders:</Text>
<View style={styles.container}>
<ScrollView>
{Object.keys(cartItem).map((menuKey) => (
<TouchableOpacity
key={menuKey}
onPress={() => {
navigation.navigate({
name: "customize",
params: {
text: cartItem[menuKey].text,
image: cartItem[menuKey].image,
cost: cartItem[menuKey].cost,
},
});
}}
>
<Swipeable
renderRightActions={(progress, dragX) => renderRightActions(progress, dragX, () => alertBeforeDelete(menuKey)
)}
ref={(ref) => (selectedMenu[menuKey] = ref)}
onSwipeableOpen={() => closeRow(menuKey)}
rightOpenValue={-100}
>
<ItemCard
text={cartItem[menuKey].text}
image={cartItem[menuKey].image}
cost ={cartItem[menuKey].cost}
/>
</Swipeable>
</TouchableOpacity>
))}
<View style={styles.priceBlock}><Text style = {styles.totalText}>Total Price: ${totalPrice}</Text></View>
<View style={styles.itemTextBlock}>
<TouchableOpacity
style={styles.clearButton}
title="clear"
color="red"
onPress={alertBeforeClear}>
<Text style={styles.buttonText}>Clear All</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.checkoutButton}
onPress={() => {
navigation.navigate('Past Orders');
clearCart();
}}
>
<Text style={styles.buttonText}>Checkout</Text>
</TouchableOpacity>
</View>
</ScrollView>
</View></>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "white",
},
loadingPage: {
flex: 1,
backgroundColor: "white",
justifyContent: "center",
},
deleteButton: {
color: "red",
backgroundColor: "#f5392f",
height: "95%",
borderRadius: 15,
justifyContent: "center",
alignItems: "center",
},
titleText: {
fontSize: 30,
fontWeight: "bold",
color: "black",
resizeMode: "contain",
textAlign: "center"
},
clearButton: {
width: "40%",
borderRadius: 25,
height: 50,
alignItems: "center",
justifyContent: "center",
backgroundColor: "grey",
},
checkoutButton: {
width: "40%",
borderRadius: 25,
height: 50,
alignItems: "center",
justifyContent: "center",
backgroundColor: "#800000",
},
buttonText: {
color: 'white',
fontWeight: 'bold'
},
itemTextBlock: {
alignItems: "center",
flex: 1,
flexDirection: "row",
justifyContent: "space-evenly",
marginBottom: 100,
},
priceBlock: {
alignItems: "center",
marginBottom: 10,
},
totalText: {
textAlign: "center",
fontSize:24,
fontWeight: "bold",
},
});
`
I have tried
parseDouble and toFixed(2)
To be honest I thought
let totalPrice = Object.keys(cartItem).map((menuKey) => (
totalPrice =+ cartItem[menuKey].cost))
would throw an error.
What I think is the best way to go about this is to use reduce to get your sum:
const initialValue = 0;
const totalPrice = Object.keys(cartItem).reduce((prevValue,currentVal)=>{
let num = parseFloat(cartItem[currentVal].cost);
// if number cant be parsed do nothing
if(isNaN(num))
return prevValue
return prevValue + num;
},initialValue);
If you want to use map:
let totalPrice = 0;
Object.keys(cartItem).map((menuKey) => {
let num = parseFloat(cartItem[menuKey].cost)
if(isNaN(num))
return
totalPrice += num
})

ERROR TypeError: undefined is not an object (evaluating 'state.map')

I am trying to change the state of my 'quantityCounter' but I'm getting an error as the title says. Can anyone help me with changing the state while the value also changes in the screen?
import React from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import { useSelector } from 'react-redux';
import { selectCartItems } from '../../../../redux/reducer/cartReducer';
import { selectAllItems } from '../../../../redux/reducer/itemReducer';
const CartList = () => {
const cartItems = useSelector(selectCartItems);
const itemData = useSelector(selectAllItems);
const [quantityCounter, setQuantityCounter] = React.useState(cartItems);
function quantityHandler({id, num}){
const targetItem = itemData.find((item) => item.id === id);
let targetCart = quantityCounter.find((cart) => cart.id === id);
setQuantityCounter((previousState) =>
previousState.forEach(
(item) => {
if(item.id === id){
Object.keys(item).find(key => {
if(key === 'quantity'){
if(num === 1 && targetCart.quantity < targetItem.availableItem){
item[key] = targetCart.quantity + 1;
}
if(num === 0 && targetCart.quantity > 0) {
item[key] = targetCart.quantity - 1;
}
}
})
}
}));
}
return (
<>
{quantityCounter.map((item) => (
<View style={styles.CartItemsContainer} key={item.id}>
<Text style={styles.textStyle}>{item.productName}</Text>
<Text style={styles.textStyle}>{item.productPrice}</Text>
<View style={styles.quantityContainer}>
<Button title='-' onPress={() => quantityHandler({id : item.id, num: 0})}/>
<Text style={styles.quantityContainer__text}>{item.itemQuantity}</Text>
<Button title='+' onPress={() => quantityHandler({id : item.id, num: 1})}/>
</View>
</View>
))}
</>
)
}
const styles = StyleSheet.create({
CartItemsContainer:{
flexDirection: 'row', alignSelf: 'stretch'
},
textStyle: {
flex: 1, alignSelf: 'stretch',
borderWidth: 1, borderTopColor: 'transparent',
textAlign: 'center', textAlignVertical: 'center'
},
quantityContainer:{
flex: 1, alignSelf: 'stretch', flexDirection: 'row',
borderWidth: 1, borderTopColor: 'transparent',
alignItems: 'baseline', justifyContent: 'center'
},
quantityContainer__text:{
marginHorizontal: 5, marginVertical: 5
}
});
export default CartList;
Another approach I did was this but the state is only changing, in the screen it doesn't. When the 'quantityHandler' is being pressed, it works as what it is supposed to be but I don't know how to fix or make this work. I tried different way but I can't really show it. Please help.
import React from 'react';
import { StyleSheet, View, Text, Button } from 'react-native';
import { useSelector } from 'react-redux';
import { selectCartItems } from '../../../../redux/reducer/cartReducer';
import { selectAllItems } from '../../../../redux/reducer/itemReducer';
const CartList = () => {
const cartItems = useSelector(selectCartItems);
const itemData = useSelector(selectAllItems);
const [quantityCounter, setQuantityCounter] = React.useState(0);
let total = 0;
let id, quantity, name, price;
let cart_replica = [];
cartItems.forEach(item => {
id = item.id;
name = item.productName;
price = item.productPrice;
quantity = item.itemQuantity;
total += item.totalPrice;
cart_replica.push({id, name, quantity, price})
});
function quantityHandler({id, num}){
const targetItem = itemData.find((item) => item.id === id);
let targetCart = cart_replica.find((cart) => cart.id === id);
cart_replica.map(
(item) => {
if(item.id === id){
return { ...cart_replica, item: { ...item, quantity: item.quantity + 1}};
}
});
console.log(cart_replica[0])
}
return (
<>
{cart_replica.map((item) => (
<View style={styles.CartItemsContainer} key={item.id}>
<Text style={styles.textStyle}>{item.name}</Text>
<Text style={styles.textStyle}>{item.price}</Text>
<View style={styles.quantityContainer}>
<Button title='-' onPress={() => quantityHandler({id : item.id, num: 0})}/>
<Text style={styles.quantityContainer__text}>{item.quantity}</Text>
<Button title='+' onPress={() => quantityHandler({id : item.id, num: 1})}/>
</View>
</View>
))}
</>
)
}
const styles = StyleSheet.create({
CartItemsContainer:{
flexDirection: 'row', alignSelf: 'stretch'
},
textStyle: {
flex: 1, alignSelf: 'stretch',
borderWidth: 1, borderTopColor: 'transparent',
textAlign: 'center', textAlignVertical: 'center'
},
quantityContainer:{
flex: 1, alignSelf: 'stretch', flexDirection: 'row',
borderWidth: 1, borderTopColor: 'transparent',
alignItems: 'baseline', justifyContent: 'center'
},
quantityContainer__text:{
marginHorizontal: 5, marginVertical: 5
}
});
export default CartList;
Can you check this ,ive added state manipulation.
Hope it helps :)
https://snack.expo.dev/5vfUoenH3
import React from 'react';
import { StyleSheet, View, Text, Button, SafeAreaView } from 'react-native';
const App = () => {
const [quantityCounter, setQuantityCounter] = React.useState([
{
id: 1,
name: 'item 1',
availableItem: 5,
price: 500,
quantity: 5,
},
{
id: 2,
name: 'item 2',
availableItem: 4,
price: 500,
quantity: 4,
},
{
id: 3,
name: 'item 3',
availableItem: 3,
price: 500,
quantity: 3,
},
]);
const quantityHandler = (id,index,isIncrement) =>{
const newCopy = [...quantityCounter];
if(isIncrement){
newCopy[index].quantity = newCopy[index].quantity +1;
}else {
newCopy[index].quantity = newCopy[index].quantity -1;
}
console.log("er",newCopy,index)
setQuantityCounter(newCopy)
}
return (
<SafeAreaView style={styles.safeStyle}>
{quantityCounter.map((item,index) => (
<View style={styles.CartItemsContainer} key={item.id}>
<Text style={styles.textStyle}>{item.name}</Text>
<Text style={styles.textStyle}>{item.price}</Text>
<View style={styles.quantityContainer}>
<Button title='-' onPress={() => quantityHandler(item.id,index,false)}/>
<Text style={styles.quantityContainer__text}>{item.quantity}</Text>
<Button title='+' onPress={() => quantityHandler(item.id,index,true)}/>
</View>
</View>
))}
</SafeAreaView>
)
}
const styles = StyleSheet.create({
safeStyle: {
marginTop:'5%'
},
CartItemsContainer:{
flexDirection: 'row', alignSelf: 'stretch'
},
textStyle: {
flex: 1, alignSelf: 'stretch',
borderWidth: 1, borderTopColor: 'transparent',
textAlign: 'center', textAlignVertical: 'center'
},
quantityContainer:{
flex: 1, alignSelf: 'stretch', flexDirection: 'row',
borderWidth: 1, borderTopColor: 'transparent',
alignItems: 'baseline', justifyContent: 'center'
},
quantityContainer__text:{
marginHorizontal: 5, marginVertical: 5
}
});
export default App;

Custom React Native list component is not handling pagination correctly

I have a custom list component called TableList. When a user clicks on an arrow it should increment to the next data set. However, when the first click in either direction is made, the component only updates the page number, not the data.
For example: when on page one, clicking next increments to page two, but the data remains the same. Clicking again, increments both the page number and the data. It will continue to paginate correctly, until the user starts clicking previous. Then it will give the same behavior as before for the first click, and continue normally for subsequent clicks.
The primary pagination function:
function changePage(direction) {
if (
(currentPage === 0 && direction === 'previous') ||
currentPage * pageSize > data
) {
return null;
}
if (direction === 'previous' && currentPage > 0) {
const newPage = currentPage - 1;
setDataSlice(data.slice(newPage * pageSize, currentPage * pageSize));
setCurrentPage(newPage);
}
if (direction === 'next') {
const newPage = currentPage + 1;
setDataSlice(data.slice(currentPage * pageSize, newPage * pageSize));
setCurrentPage(newPage);
}
}
Full code:
import {faArrowRight, faArrowLeft} from '#fortawesome/free-solid-svg-icons';
import {FontAwesomeIcon} from '#fortawesome/react-native-fontawesome';
import {func, number, string, shape} from 'prop-types';
import React, {useState} from 'react';
import {View, Text, FlatList} from 'react-native';
import EtherButton from '../components/EtherButton';
import {useTheme} from '../context/ThemeContext';
PageMenu.propTypes = {
onNext: func.isRequired,
onPrev: func.isRequired,
page: number.isRequired,
};
function PageMenu({onNext, onPrev, page}) {
const {style, values} = useTheme(getThemedStyles);
return (
<View style={style.pageMenuContainer}>
<EtherButton style={style.arrowButton} onPress={onPrev}>
<FontAwesomeIcon icon={faArrowLeft} color={values.BGSECOND} size={30} />
</EtherButton>
<View>
<Text style={style.counter}>{page}</Text>
</View>
<EtherButton style={style.arrowButton} onPress={onNext}>
<FontAwesomeIcon
icon={faArrowRight}
color={values.BGSECOND}
size={30}
/>
</EtherButton>
</View>
);
}
export default function TableList({
data,
pageSize,
style: overrides,
pageView = true,
}) {
const {style} = useTheme(getThemedStyles);
const [loadCount, setLoadCount] = useState(pageSize);
const [currentPage, setCurrentPage] = useState(0);
const [dataSlice, setDataSlice] = useState(data.slice(0, pageSize));
ListItem.proptypes = {
itemData: shape({
date: string.isRequired,
name: string.isRequired,
orderNumber: string.isRequired,
total: number.isRequired,
}),
};
function ListItem({orderData}) {
const [mouseHover, setMouseHover] = useState(false);
const textHighlight = [
style.valueText,
mouseHover ? style.textHighlighted : null,
];
return (
<View
onMouseOver={() => setMouseHover(true)}
onMouseLeave={() => setMouseHover(false)}
style={[style.listRow, mouseHover ? style.rowHighlighted : null]}
>
<View style={style.amountCell}>
<Text style={textHighlight}>{orderData.total} </Text>
</View>
<View style={style.nameCell}>
<Text style={textHighlight}>{orderData.name}</Text>
</View>
<View style={style.tokenCell}>
<Text style={textHighlight}>{orderData.orderNumber}</Text>
</View>
<View style={style.dateCell}>
<Text style={textHighlight}>
{new Date(orderData.date).toLocaleString()}
</Text>
</View>
</View>
);
}
function loadMore() {
const newCount = loadCount + pageSize;
setLoadCount(newCount);
setDataSlice(data.slice(0, newCount));
}
function changePage(direction) {
if (
(currentPage === 0 && direction === 'previous') ||
currentPage * pageSize > data
) {
return null;
}
if (direction === 'previous' && currentPage > 0) {
const newPage = currentPage - 1;
setDataSlice(data.slice(newPage * pageSize, currentPage * pageSize));
setCurrentPage(newPage);
}
if (direction === 'next') {
const newPage = currentPage + 1;
setDataSlice(data.slice(currentPage * pageSize, newPage * pageSize));
setCurrentPage(newPage);
}
}
return (
<View style={[style.mainContainer, overrides]}>
<View style={style.topRow}>
<View style={style.amountCell}>
<Text style={style.headerText}>Price</Text>
</View>
<View style={style.nameCell}>
<Text style={style.headerText}>Description</Text>
</View>
<View style={style.tokenCell}>
<Text style={style.headerText}>Order ID</Text>
</View>
<View style={style.dateCell}>
<Text style={style.headerText}>Date</Text>
</View>
</View>
<FlatList
data={dataSlice}
key={dataSlice}
renderItem={({item}) => <ListItem orderData={item} />}
keyExtractor={(item) => item.orderNumber}
style={style.flatList}
/>
<Text style={style.timeZone}>
Time shown in {new Date().toString().match(/([A-Z]+[-][0-9]+.*)/)[0]}
</Text>
<View style={style.bottomBar}>
{pageView ? (
<PageMenu
onPrev={() => changePage('previous')}
onNext={() => changePage('next')}
page={currentPage + 1}
/>
) : (
<EtherButton onPress={loadMore} style={style.loadMoreButton}>
<Text style={style.buttonText}>Load More</Text>
</EtherButton>
)}
</View>
</View>
);
}
const getThemedStyles = (theme, fontSize) => ({
mainContainer: {
backgroundColor: theme.BGFIRST,
borderColor: theme.FIRST,
borderWidth: 2,
borderRadius: 5,
overflowX: 'auto',
},
topRow: {
backgroundColor: theme.SECOND,
height: 40,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
borderTopLeftRadius: 2,
borderTopRightRadius: 2,
},
headerText: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.body,
color: theme.LIGHT,
alignSelf: 'center',
},
flatList: {
paddingVertical: 20,
},
pageMenuContainer: {
flexDirection: 'row',
alignItems: 'center',
},
counter: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.body,
backgroundColor: theme.BGSECOND,
color: theme.DARK,
padding: 5,
borderRadius: 5,
borderColor: theme.FIRST,
borderWidth: 2,
},
arrowButton: {
borderColor: theme.SECOND,
},
bottomBar: {
alignItems: 'center',
backgroundColor: theme.SECOND,
},
loadMoreButton: {
justifyContent: 'center',
alignItems: 'center',
height: 40,
marginVertical: 5,
width: '15%',
borderRadius: 5,
borderColor: theme.FIRST,
borderWidth: 2,
backgroundColor: theme.BGSECOND,
},
loadMoreText: {
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.subheader,
},
buttonText: {
fontFamily: 'NotoSans_Bold',
fontSize: fontSize.legal,
color: theme.FIRST,
textAlign: 'center',
},
// Table
listRow: {
alignItems: 'center',
flexDirection: 'row',
height: 33,
justifyContent: 'space-around',
},
rowHighlighted: {
backgroundColor: theme.SECOND,
},
valueText: {
alignSelf: 'center',
fontFamily: 'NotoSans_Regular',
fontSize: fontSize.legal,
},
textHighlighted: {
color: theme.LIGHT,
},
amountCell: {
width: 80,
minWidth: 60,
},
nameCell: {
width: 220,
minWidth: 210,
},
tokenCell: {
width: 200,
minWidth: 150,
},
dateCell: {
width: 140,
minWidth: 115,
},
//
timeZone: {
alignSelf: 'center',
fontFamily: 'NotoSans_Bold',
fontSize: fontSize.legal,
color: theme.DARK,
marginBottom: 20,
},
});
Could you please try putting changePage into a useCallback and providing dataSlice as a dependency? It looks like a data leak to me.
Unrelated: you shouldn't use key if you are using the keyExtractor. I would remove the key prop from the FlatList.
I'm embarrassed that it took me this long to figure it out, but I found the answer. I was basing the data set off the wrong variables. Changing the setState in the if (direction === "next") section fixed it. Here's what I changed it to:
setDataSlice(data.slice(newPage * pageSize, (newPage + 1) * pageSize));

Categories