clickable Flat list items in React Native - reactjs

I have a flat list that displays a list of item, I would like to make each item clickable.
My render function looks like this, and it works without any issue, except that it is not clickable
render() {
return (
<FlatList
data={formatData(data, numColumns)}
style={styles.container}
renderItem={this.renderItem}
numColumns={numColumns}
/>
);
}
I have tried to do something similar to this but it is giving me an error, any idea what's wrong with my code?
<FlatList
data={formatData(data, numColumns)}
style={styles.container}
renderItem={({this.renderItem}) => (
<TouchableHighlight
onPress={() => console.log("hello")}>
</TouchableHighlight>
)}
numColumns={numColumns}
/>

You can do like this :
renderItem with TouchableOpacity. Make sure to import it from react native.
import { TouchableOpacity } from "react-native";
import Icon from 'react-native-vector-icons/FontAwesome5';
...
...
render() {
return (
<FlatList
data={formatData(data, numColumns)}
style={styles.container}
renderItem={({item ,index}) => (
<TouchableOpacity
key={index.toString()}
onPress={() => console.log("clicked")}
>
<Icon name={item} color="red"/>
</TouchableOpacity>
)}
numColumns={numColumns}
key={numColumns.toString()} // if you want to use dynamic numColumns then you have to use key props
/>
);
}

First you need to import Touchable Opacity and the error you are getting , make sure data you are passing to flatlist is an array. So check this :
<FlatList
data={formatData(data, numColumns)} // this should be an array , or array of objects
renderItem={({item ,index}) => (
<TouchableOpacity
onPress={() => console.log("hello")}
>
<Text>Hi</Text>
</TouchableOpacity>
/>
Hope its clear

This is how you can iterate inside FlatList itself.
Working live https://snack.expo.io/#akhtarvahid/flatlist-simple
<FlatList
data={formatData(data, numColumns)}
style={styles.container}
renderItem={({item ,index}) => (
<TouchableOpacity
key={index.toString()}
onPress={() => console.log('Clicked item')}>
<Text>{'Hello'}</Text>
</TouchableOpacity>
)}
numColumns={numColumns}
keyExtractor={item=>item.id}
/>

Related

react native - key not working properly when using Array.prototype.map()

I am using Array.prototype.map() in my React Native code to render some categories in a list, each of which is editable as a TextInput. I am using a unique key at the parent element level to render the mapped array, but I am still get the following error:
Warning: Encountered two children with the same key, [object Object]
The block of code in question is the following:
const CategoriesScreen = ({ navigation }) => {
// Redux
const categories = useSelector((state) => state.auth.categories);
const dispatch = useDispatch();
return (
<ScrollView style={styles.flex}>
<View style={styles.container}>
<View style={styles.textContainer}>
<Text>Category</Text>
</View>
<View style={styles.textContainer}>
<Text>Update Priority</Text>
</View>
<Spacer />
</View>
{categories.map((category, index) => {
return (
<View key={category.id} style={styles.container}>
<View style={styles.subContainer}>
<TextInput
multiline
style={styles.textBoxes}
value={category.name}
onChangeText={(text) =>
dispatch(updateCategoryName({ index, text }))
}
/>
</View>
<View style={styles.subContainer}>
<View style={styles.icon}>
<AntDesign
name="up"
size={24}
color="black"
onPress={() => dispatch(increasePriority(index))}
/>
</View>
<View style={styles.icon}>
<AntDesign
name="down"
size={24}
color="black"
onPress={() => dispatch(decreasePriority(index))}
/>
</View>
</View>
<View style={styles.subContainer}>
<Feather
name="delete"
size={40}
color="black"
onPress={() => dispatch(deleteCategory(index))}
/>
</View>
</View>
);
})}
<AntDesign
name="plussquare"
size={40}
color="black"
style={styles.addButtonStyle}
onPress={() => dispatch(addCategory())}
/>
</ScrollView>
);
};
An example of the data array I am mapping:
const categories = [
{
"id": "dc4422e4-1fff-cf90-ce9b-1fd57348cd52",
"name": "Uncategorized"
},
{
"id": "add5cb6c-53e6-30e7-b9be-b48ad394e216",
"name": "new1"
},
{
"id": "3fa3cb68-38d5-f54b-2cc0-f0584089c1c2",
"name": "new2"
},
]
You can see that each id is completely unique. Would anyone be able to help me understand why I am getting this key error?
nachonate
When you are rendering React components in a map function inside the render function, you must provide a key prop to each component that is unique, otherwise React will put a warning in the console and may or may not update your component when it renders.
So the Solution would be a key prop should be unique, stable and reproducible.
{categories.map((category, index) => {
return (
<View category={category} key={category.id} style={styles.container}>
<View style={styles.subContainer}>
<TextInput
multiline
style={styles.textBoxes}
value={category.name}
onChangeText={(text) =>
dispatch(updateCategoryName({ index, text }))
}
The best practice in situations like this is to use the unique ID backing your objects.
You can remove this warning by using index as key
{categories.map((category, index) => {
return (
<View key={index.toString()} style={styles.container}>
the warning will disappear.
When you use database id or the object Id the map doesn't know the id is unique so it returns the warning

How to get the number of items rendered in a Flat list?

I have a flatlist that sends data to another component where the data got filtered, then the flatlist renders the filtered items. I want to get the number of items rendered by the flatlist (i.e the number of remaining items after the data got filtered). How should I do it, please?
Here is the flatlist
<FlatList
style={styles.scrollContainer}
data={this.state.schedules}
keyExtractor={item => item.id.toString()}
horizontal={false}
showsHorizontalScrollIndicator={false}
renderItem={({ item }) => <Today schedule={item} updateSchedule={this.updateSchedule}/>}
keyboardShouldPersistTaps="always"
/>}
And the component that filters the data;
{schedule.once && today == schedule.date ?
<TouchableOpacity
onPress={() => this.toggleScheduleModal()}
>
<View style={[styles.listContainer, {borderLeftWidth: 4}, {borderLeftColor: schedule.color}]}>
<View style={styles.time}>
<Text style={styles.timeText}>{schedule.stime}</Text>
<Text style={styles.timeText}>{schedule.etime}</Text>
</View>
<View style={styles.title}>
<Text style={styles.noteText} numberOfLines={1}>
{schedule.name}
</Text>
<Text style={styles.subtitle} numberOfLines={1}>
{schedule.type}
</Text>
</View>
<View style={styles.day}>
<Text style={styles.subtitle} numberOfLines={1}>
Today
</Text>
<Text style={styles.subtitle} numberOfLines={1}>
{schedule.teacher}
</Text>
</View>
</View>
</TouchableOpacity>
:
[]
}
The data got filtered and is rendered as expected. I just want to know how to count the numbers. Help please.
I believe you could use the method Children.toArray(children) to count how many rendered items there are.
I couldn't figure out where you'd use it in your specific structure, but you'd need to pass the childrenprop to that method. Children, in this case, I believe would be the component you're filtering your data in.
Another option would be to filter the data before rendering you component, which then would just be a matter of getting the array length.
const filterData = () => {
// need to define `today` here
const filteredSchedules = this.state.schedules.filter(schedule => schedule.once && today == schedule.date);
console.log(filteredSchedules.length);
return filteredSchedules;
}
<FlatList
style={styles.scrollContainer}
data={filterData()}
keyExtractor={item => item.id.toString()}
horizontal={false}
showsHorizontalScrollIndicator={false}
renderItem={({ item }) => <Today schedule={item} updateSchedule={this.updateSchedule}/>}
keyboardShouldPersistTaps="always"
/>}
You can try this current rendered index.
onViewableItemsChanged = ({ viewableItems, changed }) => {
console.log("Visible items are", viewableItems);
console.log("Changed in this iteration", changed);
}
<FlatList
onViewableItemsChanged={this.onViewableItemsChanged }
viewabilityConfig={{
itemVisiblePercentThreshold: 50
}}
/>

React Native Flatlist header re-rendering when scroll

I am new to React Native and I am having problems with the header in a FlatList.
The header re-renders as soon as I start to scroll, this creates a flickering effect on the images I have in the header.
I have been searching for an answer everywhere but I have not find a posible solution.
¿how could I configure it to stop re-rendering the header when scrolling the list?
....
const Item = ({ title }) => {
return (
<View style={styles.card}>
<Text style={styles.title}>{title}</Text>
</View>
);
};
const listHeader = () => {
const categoriesButtons = categories.map(cat => (
<CategoryButton
text={cat.name}
icon={cat.code}
key={cat.code}
onPress={() => {
//#Todo logic to filter promotions accordingly with the category pressed
}}
/>
));
return (
<View>
<View style={styles.filtersContainer}>
<ImageBackground
source={images.bgShape}
style={{ width: '100%', height: 140 }}
resizeMode="stretch"
>
<ScrollView horizontal showsHorizontalScrollIndicator={false}>
{categoriesButtons}
</ScrollView>
</ImageBackground>
</View>
<View style={styles.breadcrumbContainer}>
<Breadcrumbs navigation={navigation} stack={routes} />
</View>
<View style={styles.titleContainer}>
<Text style={sharedStyles.titleText} id="main-title">
¡{totalOfPromotions} promociones activas en Medellín!
</Text>
</View>
</View>
);
};
return (
<LinearGradient
colors={[Colors.BG_START, Colors.BG_END]}
style={styles.mainContainer}
>
{loading ? (
<ActivityIndicator size="large" color="#000000" />
) : (
<FlatList
data={promos}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={listHeader}
showsVerticalScrollIndicator={false}
onEndReached={showPromos}
onEndThreshold={0.7}
/>
)}
</LinearGradient>
);
};
listHeader() function is being called more than once because in Flatlist tag should be called as
<FlatList
data={promos}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor={(item, index) => index.toString()}
ListHeaderComponent={listHeader()}
showsVerticalScrollIndicator={false}
onEndReached={showPromos}
onEndThreshold={0.7}
/>
Use () while assigning ListHeaderComponent prop. By this way, function will be invoked only once.
Hope this help you. Enjoy coding!
From what I can see in the code you provided that you are defining the ListHeader component inside your other parent component which will redfine it on every render.
Moving it might outside the parent component might help.
You can fix your flickering issue by memoizing your ListHeaderComponent.
In your case just wrap your component with useMemo:
const listHeader = useMemo(() => {
...
})

Call not call a function inside a FlatList in react native

I am developing an App using react native. I want to pass a parameter to a function in my flatlist for a specific record. But, the flat list ignore this line of code:
onPress={ () => {console.log("TEST1"); this.onPressTopUp()} } />
Here is my code:
<FlatList
ItemSeparatorComponent={ () => <View style={ styles.rowSep } /> }
horizontal={false}
data={this.state.result}
renderItem={
({item}) => (
<View style={styles.containerrow}>
<View style={styles.viewPark}>
<Text style={styles.itemBold}> {I18n.t('Data_e_ora_inizio')}:
<Text style={styles.itemNormal}>{item.start}</Text></Text>
<Text style={styles.itemBold}> {I18n.t('Data_e_ora_termine')}:
<Text style={styles.itemNormal}>{item.end}</Text></Text>
<Text style={styles.itemBold}> {I18n.t('Energia')}: <Text style={styles.itemNormal}>{item.energy_delivered}</Text></Text>
<Text style={styles.itemBold}> {I18n.t('Colonna')}: <Text style={styles.itemNormal}>{item.column_id}</Text></Text>
<Text style={styles.itemBold}> {I18n.t('Costo_della_ricarica')}:
<Text style={styles.itemNormal}>€ {item.amount}</Text></Text>
<Text style={styles.itemBold}> {I18n.t('Aggiornamento_del')}:
<Text style={styles.itemNormal}>{item.last_update}</Text></Text>
</View>
<View style={styles.rowCenter}>
<Button label={I18n.t('Via_questa_ricarica')} color={defStyleValues.RechargeElectricCar} onPress={ () => {console.log("TEST1"); this.onPressTopUp()} } />
</View>
</View>
)
}
keyExtractor={item => item.id}
/>
Also here is my function:
onPressTopUp(columnID){
console.log("TEST2, ", columnID);
}
In other words, My problem is that I need to pass columnID of each specific row to the onPressTopUp(columnID) function in the FlatList. I checked the console log, even it dose't show both TEST1 and TEST2. Can you help me to do that?
Just use Arrow Function , it will take care of binding.
your function is :-
onPressTopUp(columnID){
console.log("TEST2, ", columnID);
}
Replace with Arrow Function :- and it will work for you.
onPressTopUp = (columnID) => {
console.log("Test, ", columnID);
}
and change your label Property with title inside your Button
You can use Button like this below :-
import { Button } from 'react-native';
...
<Button
onPress={onPressLearnMore}
title="Learn More"
color="#841584"
accessibilityLabel="Learn more about this purple button"
/>
I think the problem is you had not bind the onPressToUp function, try this:
onPressTopUp = (columnID) => {
console.log("Test, ", columnID);
}
Is Button component imported from React Native? Because react native Button component doesn't have label property.
Did you try call function with column id like this this.onPressTopUp(15).
Hope this help.

weird results from flatlist in react native with touchable opacity

I have a FlatList in a React Native project. The touchable opacity does register as the view loads, for each row, the console prints selected item.name.. (so, without being touched) however, touching the item does not do anything.
render() {
return(
<FlatList
data={stores.databaseStore.sites.slice()}
keyExtractor={ (item, index) => item.id}
numColumns={1}
extraData={stores.databaseStore.isLoadingSites}
onRefresh={() => this.onRefresh()}
refreshing={stores.databaseStore.isLoadingSites}
renderItem={({item}) => this._renderFlatListItem(item)}
ItemSeparatorComponent={this._renderSeparator}
ListHeaderComponent={this._renderHeader}
ListFooterComponent={this._renderFooter}
/>
)
}
_renderFlatListItem(item) {
return (
<View style={styles.row}>
<TouchableOpacity onPress={this._showSiteDetails(site)}>
<View style={styles.cellLeft} >
<PivotCircle site={item}/>
</View>
</TouchableOpacity>
</View>
)
}
_showSiteDetails(site){
console.log(`selected ${site.name}`);
}
found it with the help of some gents on slack
onPress={this._showSiteDetails(site)}
should be
onPress={ () => this._showSiteDetails(item)}

Resources