I have a FlatList and I want to show a number like quantity of product. So I use a state for that. Now when I press on TouchableOpacity to change quantity to 1 working fine in console, but I can't see any change in FlatList.
constructor(props){
super(props);
this.state={
quantity : 0,
}
Increment quantity:
incrementCount=()=>{
if(this.state.quantity != 10){
console.log(this.state.quantity);
this.setState((prevState, props) => ({
quantity: this.state.quantity + 1,
}));
}
}
FlatList:
<FlatList
data={this.state.dataSource}
renderItem={({item}) =>
<View>
<Text>{item.title}</Text>
<Text>{this.state.quantity}</Text>
<TouchableOpacity onPress={this.incrementCount} activeOpacity={0.5}>
<AntDesign name="plus" size={15}/>
</TouchableOpacity>
</View>
}
/>
Listview is deprecated now use Flatlist instead of this.
Refer this official doc for more information react-native listview
This is the common problem about pre-increment vs post-increment in javascript (you can read more about this here: ++someVariable Vs. someVariable++ in Javascript )
You can solve this problem by simply incrementing the variable before the setState:
const quantity = this.state.quantity + 1;
this.setState({ quantity }) // this is ES6 syntax
Anyway, you should not use ListView because it's deprecated. ( https://facebook.github.io/react-native/docs/listview.html )
There are other components:
FlatList ( https://facebook.github.io/react-native/docs/flatlist )
SectionList ( https://facebook.github.io/react-native/docs/sectionlist )
VirtualizedList ( https://facebook.github.io/react-native/docs/virtualizedlist )
Related
I have a FlatList with a data prop pulling from Redux
render() {
return (
<View>
<FlatList
data={this.props.arrayOfPlacesFromRedux}
renderItem={({item}) => {.......
Whenever I dispatch changes to arrayOfPlacesFromRedux(i.e. adding or removing children), the FlatList rerenders....UNLESS I remove all children from array (i.e. make length zero).When arrayOfPlacesFromRedux changes from a positive length to a length of zero, the FlatList does not rerender.....however all other types of changes to array do indeed cause FlatList to rerender
UPDATE 02/27
Below is my reducer used to update Redux arrayOfPlacesFromRedux
const reducer = (state = initialState, action) => {
switch (action.type) {
case UPDATE_PLACES_ARRAY:
return {...state, arrayOfPlaces: action.payload};
default:
return state;
}
};
In the situation noted above when FlatList does not rerender.....action.payload is an empty array
The question is missing some important piece of code.
React as well as Redux need arrays reference to change, meaning for a component to reRender on state change, the array references needs to change.
Live demo at https://snack.expo.dev/RrFFxfeWY
Here is the most interesting parts:
If you have a basic component as below:
const MyList = () => {
const [data, setData] = React.useState([
'#FF0000',
'#FF8000',
'#FFFF00',
]);
return (
<>
<Text>List poping is not working</Text>
<FlatList
data={data}
renderItem={({ item }) => (
<Pressable
onPress={() => {
data.pop(); // Does not work because we are not changing it's ref
}}
style={{ backgroundColor: item, padding: 8 }}>
<Text>{item}</Text>
</Pressable>
)}
/>
</>
);
};
The data need to have a new array reference as below. data2.filter(..) will return a new array, we are not changing the data2 base values, just creating a new array with one item less.
const MyList = () => {
const [data2, setData2] = React.useState([
'#00FFFF',
'#0080FF',
'#0000FF',
]);
return (
<>
<Text>List WORKING!</Text>
<FlatList
data={data2}
renderItem={({ item }) => (
<Pressable
onPress={() => {
setData2(data2.filter(dataItem => dataItem !== item)) // works
//setData2([]); // Also works
}}
style={{ backgroundColor: item, padding: 8 }}>
<Text>{item}</Text>
</Pressable>
)}
/>
</>
);
};
A library like Immer.js simplify the manipulation of states to mutate the object, and immer will created a new reference for you.
Oh no rookie mistake that wasted everyones time!!
I was implementing shouldComponentUpdate method that was stopping Flatlist rendering :(
Thanks for all for the answers
You may need to use ListEmptyComponent, which is a prop that comes with FlatList, src.
Honestly, I'm not sure why it does not re-render when you update your state, or why they added a specific function/prop to render when the array is empty, but it's clear from the docs that this is what's needed.
You can do something like this:
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
renderItem={renderItem}
keyExtractor={(item) => item.id}
extraData={selectedId}
--> ListEmptyComponent={() => <MyComponent />}
/>
</SafeAreaView>
I'm reading barcodes and every barcode I read I add to an array and show in flatlist. but after 30 barcodes adding to the array getting slow. is there any solution I can do?
renderItem:
const renderItem = useCallback(
({item, index}) => (
<View style={styles.ListItemContainer}>
<Text>
-{item} index: {index}
</Text>
<TouchableOpacity
onPress={() => {
setRemovedItem(index);
setShowAlert(true);
}}>
<Text style={{fontSize: 20, fontWeight: 'bold'}}>X</Text>
</TouchableOpacity>
</View>
),
[],
);
FlatList component:
<FlatList
renderItem={renderItem}
data={barcodeArray}
style={styles.ListContainer}
keyboardShouldPersistTaps="handled"
initialNumToRender={12}
removeClippedSubviews
windowSize={12}
maxToRenderPerBatch={12}
/>
adding barcode:
const readBarcode = barcode => {
setbarcodeArray([barcode, ...barcodeArray]);
setbarcodeValue('');
setkey(key + 1);
};
for this solution you can use VirtualizedList instead Flatlist . In general, this should only really be used if you need more flexibility than FlatList .
for more info see this
Did you try using this: https://github.com/Flipkart/recyclerlistview library. It renders far fewer items than FlatList and then recycles them. Should be must faster and more performant than the native flatlist. If this does not work then try to use getItemLayout in flatlist if you have a fixed height of the content.
How can I create an flatlist and add items to it inside a function not a class in react native?? all of the examples online are using classes and I need to use it inside a function !!
I found an example of a FlatList in the React Native docs that is using a functional component:
https://reactnative.dev/docs/flatlist
If you just want the code check out the same example on snack:
https://snack.expo.io/?session_id=snack-session-R6Nsz_Qm1&preview=true&platform=web&iframeId=uetjvvask3&supportedPlatforms=ios,android,web&name=flatlist-simple&description=Example%20usage&waitForData=true
I hope it helped :)
Same as with any other component, there's not much difference between using a FlatList inside a class vs a function. Only the state handling changes a little bit.
The code below will render all items, you'll be able to press on any of them to duplicate the item which should then show up at the bottom of the list.
export const FlatListScreen = props => {
const [items, setItems] = useState([1, 2, 3, 4, 5]);
function duplicateItem(toDuplicate) {
setItems(prev => [...prev, toDuplicate]);
}
return (
<FlatList
data={items}
renderItem={({ item }) => (
<TouchableWithoutFeedback onPress={() => duplicateItem(item)}>
<View>
<Text>
{item}
</Text>
</View>
</TouchableWithoutFeedback>
)}
/>
);
}
This a simple FlatList:
class Products ..
render() {
return (
<FlatList
renderItem={this._renderItem}
);
}
I want to create a list of items and navigate to Detail Page by onPress items.
Can Please tell me which method is better?
Method 1:
Insert navigate to Detail page in child component(CardProduct component) like this:
_renderItem = ({item}) => (
<CardProduct
id={item.id}
title={item.title}
/>
);
and in CardProduct component:
render() {
const { id,title } = this.props;
return (
<Card style={{flex:1}}>
<CardItem cardBody button onPress={() => this.props.navigation.navigate('Details',{productId:id})}>
...
);
}
Method 2:
Insert navigate to Detail page in current component(Products component) like this:
_onPressItem = (id: string) => {
this.props.navigation.navigate('Details',{productId:id});
};
_renderItem = ({item}) => (
<CardProduct
id={item.id}
title={item.title}
onPressItem={this._onPressItem}
/>
);
and in CardProduct component:
_onPress = () => {
this.props.onPressItem(this.props.id);
};
render() {
const { id,title } = this.props;
return (
<Card style={{flex:1}}>
<CardItem cardBody button onPress={this._onPress}>
...
);
}
I used to do the method 1, but I read this guide.
Short answer:
You should go for method2.
Explanation:
In method1 you are using an arrow function in CardItem's onPress, so everytime CardProduct is re-rendered a new reference of onPress is created, which forces CardItem to re-render, even if all the other props are staying the same. In method 2 you are binding the function to context, which won't force a re-rendering of the CardItem.
By the way, in general it is a good idea to prevent the usage of arrow functions in render().
One step for performance optimization in react-native flatlist, is using a stateless functional component for the renderItem. and you should always give each item a unique key.
Is there a provided method to enable us to get the index of the top item of the FlatList when we scroll it ?
You can access the index of each row in this way:
<FlatList data={this.state.dataList}
...
renderItem={({item, index}) => this.renderRow(item, index)}
...
console.log("current index is " + index)
/>
this will give you the index of the top row.
there is no method to get it directly.
but you can compute it by scrollview offset, if you know the layout of every item.
implement onScroll on flatlist, like
<FlatList onScroll={(e)=>{
let offset = e.nativeEvent.contentOffset.y;
let index = parseInt(offset / height); // your cell height
console.log("now index is " + index)
}}>
ps: don't forget to subtract the header layout if contains
Here is the right way to get index of items of flat list:
<FlatList
horizontal
data={this.state.data}
renderItem={({ item: rowData,index }) => {
return (
<Card
containerStyle={{flex:1,marginBottom:30,backgroundColor:'rgba(255,255,255,0.7)',}}>
<Text style={{ marginBottom: 10 ,fontSize:30}}>
{'Comments:'}
</Text>
<Text style={{width:290}}>
{rowData.comment}
</Text>
<Text style={styles.text }>
{'\nIndex:'+index}
</Text>
</Card>
);
}}
keyExtractor={(item,index) => index.toString()}
/>
Actually you have to use the prop onViewableItemsChanged and get from the data returned by the callback the index of the first element displayed in the list. Remember to set also the prop viewabilityConfig so the component can determine where you consider an object displayed or not.
import React from "react";
import SwiperFlatList from "react-native-swiper-flatlist";
export default class MyComponent extends React.Component {
// INSIDE A COMPONENT
constructor(props) {
super(props);
this.currentIndex = 0;
this._updateIndex = this._updateIndex.bind(this);
this.viewabilityConfig = {
itemVisiblePercentThreshold: 50
};
}
_updateIndex({ viewableItems }) {
// getting the first element visible index
this.currentIndex = viewableItems[0].index;
}
render() {
return // ...
<SwiperFlatList
loop={false}
index={0}
onViewableItemsChanged={this._updateIndex}
viewabilityConfig={this.viewabilityConfig}
>
{data}
</SwiperFlatList>;
}
}
instead of swiper-flat-list you can use the FlatList.
In order to get an index of FlatList item - spread the itemData inside renderItem.
<FlatList
data={someData}
keyExtractor={(item, index) => index.toString()}
renderItem={ ( {item, index} ) => // use {item, index} instead of itemData
<MyComponent index={index} />
/>
View has an onLayout event which you could use to receive the x and y for the element. This is likely to require you to pass in a function in prop that will then receive the updates from each view in your flatlist. What are yo utrying to use it for?
For those who want an easy way, react-native-swiper-flatlist provides getCurrentIndex and OnIndexChange methods to find index.