Why does FlatList scrolls to top when I select an item - react-native

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}

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} />
})}

Correct way to use memo in Flatlist react native

Good evening everyone .
I have created the following Flatlist :
<View style={{ flex: 1 }}>
<FlatList
ListFooterComponent={
loadMore && page < pageMax ? (
<ActivityIndicator color={Colors.grey40} />
) : (
<View />
)
}
ListFooterComponentStyle={{ height: 200 }}
contentContainerStyle={styles.listExercise}
keyExtractor={() => uuid.v4()}
data={exercises}
renderItem={renderItemRE}
removeClippedSubviews={true}
onEndReached={() => {
setOnEndReachedCalledDuringMomentum(true);
}}
onMomentumScrollEnd={loadMoreFc}
onEndReachedThreshold={0.3}
ListEmptyComponent={
<Text contentW B18>
Nessuna Esercizio presente
</Text>
}
/>
</View>
This is renderItem function
const renderItemRE = ({ item }) => {
return (
<RenderItemRE
selectables={route.params?.selectables}
item={item}
navigation={navigation}
/>
);
};
And finally this is my component RenderItemRE
const RenderItemRE = ({ item, navigation, selectables }) => {
return (
<View style={styles.globalContainer}>
<TouchableOpacity
style={styles.touchable}
onPress={() => {
navigation.navigate(Routes.InfoEsercizio, {
id_ex: item.id,
nomeEx: item.nome,
});
}}
>
<View style={styles.container}>
<Image
indicator={Progress}
style={styles.img}
source={{
uri: item.galleria
? item.galleria[0]
? item.galleria[0]
: "logo"
: ApiConstants.DEFAULT_IMAGE,
}}
/>
<Text
style={[
customtext(DYNAMIC_FONTS_SIZE.FONT_SIZE_BIG).regular,
styles.nameStyle,
]}
>
{item.nome}
</Text>
</View>
</TouchableOpacity>
</View>
);
};
function arePropsEqual(prevProps, nextProps) {
return nextProps.item.id === prevProps.item.id;
}
export default memo(RenderItemRE, arePropsEqual);
This is a correct way to use memo in react native ? Or am I doing something wrong? I'm noticing that when the list gets bigger the rendering slows down. I was wondering what was the correct way to optimize. Thank you in advance

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>
);
};

React Native Flatlist with radio buttons works slow

I have flatlist with custom radio buttons implementaion, but when i tap on flatlist item it waits about second before changes to active button. I tried to useCallback with my renderItem and itemKeyExtractor functions but it doesnt help me.
Here is the code of my screen component:
export const PickOrganizationScreen = (props) => {
const { navigation, setOrganization, loadProcedureProviders, items, isLoading, procedureId, organizationId } = props;
useFocusEffect(
React.useCallback(()=>{loadProcedureProviders(procedureId);},[loadProcedureProviders, procedureId])
);
const renderItem = ({item}) => {
return (
<OrganizationItem
title={item.Title}
checked = {item.Id === organizationId}
onPress={() => {
setOrganization(item.Id, item.Title);
}}
/>);
};
const itemKeyExtractor = (item) => item.Id;
return (
isLoading ? (
<Spinner />
) : (
items.length > 0 ? (
<View style={styles.container}>
<View style={styles.paragraph}>
<Text style={styles.subtitle}>{I18n.t('pickOrganization')}</Text>
</View>
<View style={styles.alertMessage}>
<View style={styles.alertIcon}>
<Icon name="info" width={32} height={32} fill={Colors.primaryRed} />
</View>
<View>
<Text style={styles.alertText}>{I18n.t('pickOrganizationToRegisterService')}</Text>
</View>
</View>
<FlatList
data = {items}
renderItem = {renderItem}
keyExtractor = {itemKeyExtractor}
extraData={organizationId}
/>
<View style={styles.buttonWrapper}>
<ScreenButton
title={I18n.t('goToRegister')}
onPress={ () => {
navigation.navigate('ServiceSummary');
}}/>
</View>
</View>
) : (
<NotFound extraText={I18n.t('notFoundExtra')}/>
)
)
);
};
Here is the code of flatlist item component:
export const OrganizationItem = ({title, checked, onPress}) => {
return (
<TouchableOpacity onPress={onPress}>
<View style={styles.itemContainer}>
<View style={styles.itemIcon}>
{ checked ? (
<Icon name="radio-button-on" width={16} height={16} fill={Colors.primaryRed} />
) :
(
<Icon name="radio-button-off" width={16} height={16} fill={Colors.gray} />
)
}
</View>
<View style={styles.itemText}>
<Text style={styles.title}>{title}</Text>
</View>
</View>
</TouchableOpacity>
);
};
Try memoizing OrganizationItem. Does the onPress in OrganizationItem change the items object ? If so, it will re-render the whole Flatlist even when one small change is made. If you memoize, the components memoized won't rerender unless the props passed to it changes.
export const OrganizationItem = React.memo(({title, checked, onPress}) => {
return (
<TouchableOpacity onPress={onPress}>
<View style={styles.itemContainer}>
<View style={styles.itemIcon}>
{ checked ? (
<Icon name="radio-button-on" width={16} height={16} fill={Colors.primaryRed} />
) :
(
<Icon name="radio-button-off" width={16} height={16} fill={Colors.gray} />
)
}
</View>
<View style={styles.itemText}>
<Text style={styles.title}>{title}</Text>
</View>
</View>
</TouchableOpacity>
);
});

Flatlist not updating when the data is changed

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