Flatlist not updating when the data is changed - react-native

I have an issue when I update the item's Quantity then set array, FlatList is not Updating
also, extraData is not working for me I have tried so many things to fix it, but still not working
check the code for adding quantity function below:
const handleAdd = (message) => {
//add Quantity for items
let items = list;
let index = items.findIndex((el) => el._id == message._id);
items[index] = {
...items[index],
sellingQuantity: parseInt(items[index].sellingQuantity) + 1,
};
setList(items);
};
and here is the FlatList
<FlatList
data={list}
keyExtractor={(message) => message._id}
renderItem={({ item }) => (
<ListItemSelling
title={title}
barcode={item.barcode}
description={item.description}
price={item.price1}
quantity={item.sellingQuantity}
totalPrice={item.totalPrice}
renderLeftActions={() => (
<View style={styles.swipeLeft}>
<TouchableOpacity onPress={() => handleDelete(item)}>
<MaterialCommunityIcons
name="trash-can"
size={30}
color={colors.white}
/>
</TouchableOpacity>
</View>
)}
renderRightActions={() => (
<View style={styles.swipeRight}>
<TouchableOpacity onPress={() => handleAdd(item)}>
<MaterialCommunityIcons
name="plus"
size={30}
color={colors.white}
/>
</TouchableOpacity>
<TouchableOpacity onPress={() => handleDeduct(item)}>
<MaterialCommunityIcons
name="minus"
size={30}
color={colors.white}
/>
</TouchableOpacity>
</View>
)}
/>
)}
/>

I fixed it by adding another function to count total
const countTotal = () => {
let element = 0;
for (let i = 0; i < list.length; i++) {
element += list[i].totalPrice;
}
setTotalItems(element);
console.log(element);
};
then adding countTotal(); in the handleAdd() function

Related

How to toggle with map function

I want to use toggle with map function.
I've tried many things, but I returned to the first code. I know what's the problem(I use the map function, but I use only one toggle variable), but I don't know how to fix it.
This is my code.
const [toggle, setToggle] = useState(true);
const toggleFunction = () => {
setToggle(!toggle);
};
{wholeData.map((image)=>{
return(
<TouchableOpacity
key={image.ROWNUM}
onPress={() => navigation.navigate("DetailStore", {
contents: image,
data: wholeData
})}
>
.
.
.
<TouchableOpacity onPress={() => toggleFunction()}>
{toggle ? (
<View style={{marginRight: 11}}>
<AntDesign name="hearto" size={24} color="#C7382A" />
</View>
) : (
<View style={{marginRight:11}}>
<AntDesign name="heart" size={24} color="#C7382A" />
</View>
)}
</TouchableOpacity>
As you've noticed, using a single state for all the toggles won't give you what you want. All you have to do is move your toggle function inside the component you return when you're mapping over your images.
As an unrelated note, you could also simplify your second TouchableOpacity a bit, since the only thing that changes is the icon name.
For example:
// New component
const ListImage = ({ image }) => {
const [toggle, setToggle] = useState(true);
const toggleFunction = () => {
setToggle(!toggle);
};
return (
<TouchableOpacity
key={image.ROWNUM}
onPress={() => navigation.navigate("DetailStore", {
contents: image,
data: wholeData
})}
>
...
<TouchableOpacity onPress={() => toggleFunction()}>
<View style={{ marginRight: 11 }}>
<AntDesign
name={toggle ? "hearto" : "heart"}
size={24}
color="#C7382A"
/>
</View>
</TouchableOpacity>
)
}
// in current component
{wholeData.map((image) => {
return <ListImage image={image} />
})}

FlatList rerender all items favourite

I wanna achieve this effect: when i will press icon item will be added to favourite list and when i press again item will be deleted from favourite list but without rerender all of items from flatlist ( only rerender 1 item ). Now when i press icon all items from the list are rerender. Whats wrong? Items and list of favourite items come from api. They are two arrays of objects. I put id favourite items to array. When id of object is in array you can delete or add to favourite.
snippet of code:
const [fav, setFav] = useState([]); // array of favourites id
const SingleItem = React.memo(({ item, navigation }) => {
return(
<TouchableWithoutFeedback onPress={() => navigation.navigate("xxx", {
id: item.id,
})}>
<View style={styles.singleItemContainer}>
<View style={styles.imageContainer}>
<SliderBox images={images}
ImageComponent={ImageBackground}
ImageComponentStyle={{ width: '90%', marginTop: 10,}}
imageStyle={{borderRadius: 12}}
>
{ route.params?.isLogged &&
<View style={styles.heartIcon}>
<IconFontAwesome name={fav.includes(item.id) ? "paw" : "paw-outline"}
size={30}
color={fav.includes(item.id) ? "red" : "white"}
onPress={() => {
if(fav.includes(item.id)){ // is favourite and delete from favourite
deleteFavourite(item.id)
setFav(fav.filter(el => el != item.id))
}
else{ // add to favourite
addFavourite(item.id)
setFav([item.id, ...fav])
}
}}/>
</View>
}
</SliderBox>
</View>
</View>
</TouchableWithoutFeedback>
)});
const renderItem = ({ item }) => {
return(
<SingleItem
item={item}
navigation={navigation}
/>
);}
return (
<View style={styles.screen}>
<FlatList
data={data}
initialNumToRender={12}
renderItem={renderItem}
keyExtractor={(item) => item.id}
onEndReachedThreshold = {0.2}
onEndReached = {fetchData}
/>
</View>
);
};

Fix this React Native error VirtualizedList contains a cell which itself contains more than one VirtualizedList

When I load my React Native project, I am getting this Error:
A VirtualizedList contains a cell which itself contains more than one VirtualizedList of the same orientation as the parent list. You must pass a unique listKey prop to each sibling list.
How can I fix it?
Error Showing:
Code:
const Home = () => {
function renderRecentEvent() {
return (
<FlatList
data = {recentData}
lisKey = {item => item.id}
showsVerticalScrollIndicator = {false}
renderItem={({item}) => {
return (
<View>
<Text>{ item.event_name }</Text>
</View>
)
}}
/>
)
}
function renderMonthEvent() {
return (
<FlatList
data = {todayData}
lisKey = {item => item.id}
showsVerticalScrollIndicator = {false}
renderItem={({item}) => {
return (
<View>
<Text>{ item.event_info }</Text>
</View>
)
}}
/>
)
}
return (
<View>
<FlatList
data = {eventData}
keyExtractor = {item => item.id}
keyboardDismissMode = 'on-drag'
showsVerticalScrollIndicator = {false}
ListHeaderComponent = {
<View>
{renderRecentEvent()}
{/* --------- Month Event --------- */}
{renderMonthEvent()}
</View>
}
renderItem = { ({item}) => {
return (
<CategoryCard
categoryItem={item}
/>
)
}}
ListEmptyComponent = {
<View
style = {{ marginBottom: 100 }}
/>
}
/>
</View>
)
}
As the error says "You must pass a unique listkey prop to each sibling list".
If there are multiple VirtualizedLists at the same level of nesting within another VirtualizedList, this key is necessary for virtualization to work properly.
For example:
<FlaList>
<FlaList listKey="1.1" />
<FlaList listKey="1.2" />
</FlaList>
<FlaList>
<FlaList listKey="2.1" />
<FlaList listKey="2.2" />
</FlaList>
Update
In your example, you have a typo in the prop name. You have to write listkey not lisKey.
const Home = () => {
function renderRecentEvent() {
return (
<FlatList
data={recentData}
listKey="recent-event"
keyExtractor={item => item.id}
showsVerticalScrollIndicator={false}
renderItem={({ item }) => {
return (
<View>
<Text>{item.event_name}</Text>
</View>
);
}}
/>
);
}
function renderMonthEvent() {
return (
<FlatList
data={todayData}
listKey="month-event"
keyExtractor={item => item.id}
showsVerticalScrollIndicator={false}
renderItem={({ item }) => {
return (
<View>
<Text>{item.event_info}</Text>
</View>
);
}}
/>
);
}
return (
<View>
<FlatList
data={eventData}
keyExtractor={item => item.id}
keyboardDismissMode="on-drag"
showsVerticalScrollIndicator={false}
ListHeaderComponent={
<View>
{renderRecentEvent()}
{renderMonthEvent()}
</View>
}
renderItem={({ item }) => {
return <CategoryCard categoryItem={item} />;
}}
ListEmptyComponent={<View style={{ marginBottom: 100 }} />}
/>
</View>
);
};

Why does FlatList scrolls to top when I select an item

I have a FlatList with TouchableOpacity items. When I try to select an item, the list scrolls to top.
This is the Collapsible content;
const setBrandSelected = index => {
var temp = JSON.parse(JSON.stringify(tempFilterOptions));
temp[0].enums[index].selected = !temp[0].enums[index].selected;
setTempFilterOptions(temp);
};
const FilterOptionBrand = () => {
const RenderBrand = ({ item, index }) => {
return (
<TouchableOpacity onPress={setBrandSelected.bind(null, index)}>
{item.selected && (
<View style={[styles.brandImage, styles.selectedBrandImage]}>
<Icon iconName={iconNames.Tick} />
</View>
)}
<FastImage source={{ uri: item.logo }} style={styles.brandImage} resizeMode={FastImage.resizeMode.contain} />
<Text style={styles.brandName}>{item.name}</Text>
</TouchableOpacity>
);
};
const BrandSeparator = () => <View style={styles.brandSeparator} />;
return (
<View style={styles.filterBrandMainContainer}>
<View style={styles.searchBrandContainer}>
<View style={styles.magnifyingGlassWrapper}>
<Icon iconName={iconNames.MagnifyingGlass} />
</View>
<TextInput style={styles.searchBrandInput} placeholder={searchBrandText} />
</View>
<FlatList
horizontal={true}
showsHorizontalScrollIndicator={false}
keyExtractor={(item, index) => String(item.id)}
style={styles.collapsibleContainer}
data={tempFilterOptions[0].enums}
renderItem={RenderBrand}
ItemSeparatorComponent={BrandSeparator}
></FlatList>
</View>
);
};
If you somehow stumble upon this question. The answer is wherever the problem occurs be it Header be it Footer. You should call the component function and not directly just type the function.
const Header = () => <View />
Correct usage;
ListHeaderComponent={Header()}
Incorrect usage;
ListHeaderComponent={Header}

React Native FlatList Delete Item

I want to delete item from the FlatList. However, the solution from here is not working for my code. When I run my code, I am getting error such as 'index is not defined'.
I have yet to find other post regarding this issue so any help would be greatly appreciated.
Code snippet provided below:
export default class FrCreateScreen extends Component {
constructor(props) {
super(props);
this.state = {
//new timeSlots
timeSlots: [],
}
}
setEndTime = (event, appointmentEndTime, textEndTime) => {
appointmentEndTime = appointmentEndTime || this.state.appointmentEndTime;
textEndTime = textEndTime || moment(appointmentEndTime).format('h:mm a').toString();
this.setState({
showEndTime: Platform.OS === 'ios' ? true : false,
appointmentEndTime,
textEndTime,
timeSlots: [
...this.state.timeSlots,
{
apptdate: this.state.textAppointmentDate,
appttime: this.state.textAppointmentTime,
endTime: textEndTime,
}
],
});
}
deleteDateTime = (id) => {
const filteredData = this.state.timeSlots.filter(item => item.id !== id);
this.setState({ timeSlots: filteredData });
}
render() {
return (
<ScrollView>
...
<View>
<FlatList
data={this.state.timeSlots}
keyExtractor={({ id }, index) => index.toString()}
renderItem={({ item }) => {
return (
<View style={styles.containerList}>
...
<TouchableOpacity onPress={() => this.deleteDateTime(index)}>
<Feather name="trash" style={styles.icon} />
</TouchableOpacity>
</View>
</View>
);
}}
/>
</View>
</ScrollView>
)
};
};
Screenshot below:
I think you need to add index inside renderItem { item, index }
renderItem = {({ item, index }) => {
return (
<View style={styles.containerList}>
<TouchableOpacity onPress={() => this.deleteDateTime(index)}>
<Feather name="trash" style={styles.icon} />
</TouchableOpacity>
</View>
);
}}
While rendering in map function , it provides the index too, so try adding that.
renderItem={({ item,index }) => {
return (
<View style={styles.containerList}>
...
<TouchableOpacity onPress={() => this.deleteDateTime(index)}>
<Feather name="trash" style={styles.icon} />
</TouchableOpacity>
</View>
</View>
);
}}
Hope it helps
ok fixed. on touchable onPress, the argument should be 'item.index' instead of 'index'.
here's the correct code:
renderItem={({ item,index }) => {
return (
<View style={styles.containerList}>
...
<TouchableOpacity onPress={() => this.deleteDateTime(item.index)}>
<Feather name="trash" style={styles.icon} />
</TouchableOpacity>
</View>
</View>
);
}}