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
})
Related
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;
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.
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));
when I run my application it's okay and work If I create an array and put it in the data in FlatList like this array
const photos = [
{ id: 1, title: "Photo 1" },
{ id: 2, title: "Photo 2" },
{ id: 3, title: "Photo 3" },
{ id: 4, title: "Photo 4" },
{ id: 5, title: "Photo 5" },
{ id: 6, title: "Photo 6" },
];
But when I replace the photos array with an API, The app doesn't work. I tried more than API, I think the error is in my code not in the API,
This error appears to me " scrollToIndex out of range: request index1 but maximum is -1 "
What's wrong with my code?
import React, { useState, useRef, useEffect } from "react";
import {
StyleSheet,
View,
FlatList,
Dimensions,
Text,
TouchableOpacity,
} from "react-native";
import { AntDesign } from "#expo/vector-icons";
import axios from "axios";
const phoneWidth = Dimensions.get("screen").width;
const phoneHeight = Dimensions.get("screen").height;
function ScrollScreen() {
const [index, setIndex] = useState(0);
const [border, setBorder] = useState(0);
const refContainer = useRef();
const refBox = useRef();
const [data, setData] = useState([]);
useEffect(() => {
photos();
}, []);
function photos() {
axios
.get("https://jsonplaceholder.typicode.com/photos")
.then(async function (response) {
setData(response.data);
})
.catch((err) => console.error(err));
}
useEffect(() => {
refContainer.current.scrollToIndex({ animated: true, index });
}, [index]);
useEffect(() => {
refBox.current.scrollToIndex({ animated: true, index });
}, [index]);
const theNext = () => {
if (index < photos.length - 1) {
setIndex(index + 1);
setBorder(index + 1);
}
};
const thePrevious = () => {
if (index > 0) {
setIndex(index - 1);
setBorder(index - 1);
}
};
return (
<View style={styles.con}>
<AntDesign
style={[styles.iconConPosition, { left: phoneWidth * 0.05 }]}
onPress={thePrevious}
size={55}
color="#0dddcb"
name="caretleft"
/>
<AntDesign
style={[styles.iconConPosition, { right: phoneWidth * 0.05 }]}
onPress={theNext}
size={55}
color="#0dddcb"
name="caretright"
/>
<FlatList
scrollEnabled={false}
ref={refContainer}
data={data}
// data={photos}
keyExtractor={(item, index) => item.id.toString()}
style={styles.flatList}
renderItem={({ item, index }) => (
<View
style={{
height: 150,
width: phoneWidth * 0.7,
margin: 50,
backgroundColor: "red",
alignSelf: "center",
justifyContent: "center",
alignItems: "center",
}}
>
<Text>{item.id}</Text>
<Text>{item.title}</Text>
</View>
)}
horizontal
pagingEnabled //تفعيل خاصية التمرير
showsHorizontalScrollIndicator={false}
/>
<FlatList
ref={refBox}
data={data}
// data={photos}
keyExtractor={(item, index) => item.id.toString()}
style={styles.flatList}
renderItem={({ item, index }) => (
<TouchableOpacity
onPress={() => {
setIndex(index);
setBorder(index);
}}
style={
border === index
? {
height: 100,
width: phoneWidth * 0.4,
margin: 7,
backgroundColor: "gray",
alignSelf: "center",
justifyContent: "center",
alignItems: "center",
borderWidth: 2,
borderColor: "blue",
}
: {
height: 100,
width: phoneWidth * 0.4,
margin: 7,
backgroundColor: "gray",
alignSelf: "center",
justifyContent: "center",
alignItems: "center",
}
}
>
<Text>{item.id}</Text>
<Text>{item.title}</Text>
</TouchableOpacity>
)}
horizontal
/>
<Text>{index}</Text>
</View>
);
}
export default ScrollScreen;
Initially data is an empty array, but index is set to 0. On the first invocation of the useEffect that tries to scroll, there is an error because scrollToIndex(0) is an error when data is empty, since there is no item for index 0.
Try initializing the border and index state to -1 instead of 0 like:
const [index, setIndex] = useState(-1);
const [border, setBorder] = useState(-1);
On a separate but related note the theNext function has an error, it should be checking data.length instead of photos.length.
Ran into the same issue just now. Using ScrollToOffSet({offset: number, animated: boolean}) instead of scrollToIndex solved the issue for me
Below is the code component for the customer picker
import React, { useEffect, useState } from "react";
import { connect } from 'react-redux';
import {
TouchableOpacity,
FlatList,
SafeAreaView,
StatusBar,
StyleSheet,
Text,
View,
Button,
Alert,
} from "react-native";
import { screenHeight, screenWidth } from "../constants";
const DATA = [
{
id: "bd7acbea-c1b1-46c2-aed5-3ad53abb28ba",
title: "Client Not Found"
},
{
id: "58694a0f-3da1-471f-bd96-145571e29d72",
title: "Client refused"
},
];
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.item, style]}>
<Text style={styles.title}>{item.title}</Text>
</TouchableOpacity>
);
const StatusOptions = (props) => {
const [selectedId, setSelectedId] = useState(null);
const renderSeparator = () => (
<View
style={{
backgroundColor: "grey",
height: 0.8
}}
/>
);
const ListHeader = () => {
//View to set in Header
return (
<View style={{ height: 20 }}></View>
);
};
const renderItem = ({ item }) => {
const backgroundColor = item.id === selectedId ? "#6cd9ff" : "white";
return (
<Item
item={item}
onPress={() => {
setSelectedId(item.id);
console.log("SELECTED ID _ STATUSOPTIONS component : ", selectedId);
const val = DATA.filter(status => status.id == selectedId).map(filteredStatus => filteredStatus.title);
console.log("VALLLLLLLLLLLLLLLLLLLLLLLUUUUUUEEEEEEEEEEEEEEEEEEEE:::: ", val);
props.navigation.navigate('AnotherScreen');
}}
style={{ backgroundColor }}
/>
);
};
return (
<View style={{ bottom: 0, flex: 1, position: 'absolute', width: screenWidth, }}>
<View style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={ListHeader}
style={{
backgroundColor: "white",
width: "100%",
borderTopRightRadius: 20,
borderTopLeftRadius: 20,
zIndex: 1,
}}
/>
</View>
<View style={{ backgroundColor: "grey", height: 0.4 }} />
<View style={styles.closeButtonContainer}>
<TouchableOpacity style={styles.closeButton}
onPress={() => {
props.setStatusOptionsVisible(false)
}}>
<Text style={styles.title}>Close</Text>
</TouchableOpacity>
</View>
</View>
);
};
function mapStateToProps(state) {
return {
StatusOptionsVisible: state.volunteerItems.statusOptionsVisible,
currentTaskItemId: state.taskItems.taskItemId,
};
}
function mapDispatchToProps(dispatch) {
return {
setStatusOptionsVisible: (visible) => dispatch({ type: 'SET_STATUS_VISIBLE', statusVisibility: visible }),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(StatusOptions);
const styles = StyleSheet.create({
closeButton: {
backgroundColor: 'lightgrey',
borderRadius: 10,
height: 50,
justifyContent: 'center',
width: '90%',
},
closeButtonContainer: {
alignItems: 'center',
height: 90,
justifyContent: 'center',
backgroundColor: 'white',
},
textStyle: {
textAlign: "center",
},
container: {
borderRadius: 30,
flex: 4,
width: screenWidth,
},
item: {
padding: 20
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: "black",
textAlign: "center"
}
});
the console Log : ("SELECTED ID _ STATUSOPTIONS component : ", selectedId)
in render Item function returns null for first picker item selection and the returns the previous value for the next picker item selection , can anyone please help with fixing it ?
Try to use this
useEffect(() => {
console.log("SELECTED ID _ STATUSOPTIONS component : ", selectedId);
if(selectedId != null) {
const val = DATA.filter(status => status.id == selectedId).map(filteredStatus => filteredStatus.title);
console.log("VALLUUEEEEEEEEEEEEEEEEEEEE:::: ", val);
}
}, [selectedId])