react native Flatlist re rendering elements - reactjs

In the following code, there is an array of 200 elements. And I am using Flatlist to render elements of the array. But renderItems function is called for first 10 elements and then for first 20 elements including first 10 elements and then for first 30 elements including first 20 elements and so on. I expect renderItems function to be called once for each item and only for the items which are displayed on the screen. What should I change in my code?
import {View, Text, FlatList} from 'react-native';
const numbers = Array.from(Array(200), (_, i) => i);
const renderItems = (item) => {
console.log('Render Items == ', item.item);
return (
<View>
<Text
style={{
fontSize: 40,
fontWeight: '400',
textAlign: 'center',
flex: 1,
}}>
{item.item}
</Text>
</View>
);
};
const App = () => {
return (
<View
style={{
flex: 1,
width: '100%',
}}>
<FlatList
data={numbers}
renderItem={renderItems}
keyExtractor={(item) => item.toString()}
/>
</View>
);
};
export default App;```

Related

How to map over axios array to display image

I want to display an image from my data. It works when I use Flatlist but Flatlist has conflicts with ScrollView, so I had to change my displaying method from Flatlist to mapping with component.
First name renders when I use {profile.first_name}, but the image won't render. I believe the issue is in the source = {} of the Image. I have tried profile.banner_picture and that has not worked either.
const bannerPicture = () => {
return profile.map((profile) => {
return (
<View key={profile.key}
style={{padding: 1}}>
<Image
source={banner_picture}
style = {{
height: 100,
width: 100,
}}/>
<Text>{profile.first_name}</Text>
</View>
);
});
};
When I change
source = {banner_picture}
//to this
source={{uri: banner_picture}}
// it works
const bannerPicture = () => {
return profile.map((profile) => {
return (
<View key={profile.key}
style={{
flex: 1,
height: 400,
width: 400,
padding: 1}}>
<Image
source={{uri: banner_picture}}
style = {{
height: 100,
width: 100,
}}/>
<Text>{profile.first_name}</Text>
</View>
);
});
};

How to optimize FlatList in React Native

Can you please tell me how can I optimize this flatlist in react native. I mean how can I do that app will render not the whole list of data but just small part of it for example 10 items, and then when the user will scroll it down it will load more of data from list?
that's the code
import React, {useState, useEffect} from 'react';
import {
SafeAreaView,
StatusBar,
StyleSheet,
Text,
View,
FlatList,
TextInput,
} from 'react-native';
import {newdata} from '../Data/newdata';
const Sample = () => {
const DATA = newdata;
const [searchText, onChangeSearch] = useState('');
const [filteredData, setFilteredData] = useState([]);
useEffect(() => {
const filtered = DATA.filter(item =>
item.title.toLowerCase().includes(searchText.toLowerCase()),
);
if (searchText === '') {
return setFilteredData(DATA);
}
setFilteredData(filtered);
}, [searchText]);
const Item = ({title}) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const renderItem = ({item}) => <Item title={item.title} />;
return (
<SafeAreaView style={styles.container}>
<TextInput
style={{
height: 50,
borderColor: '#919191',
borderWidth: 1,
margin: 10,
paddingLeft: 15,
borderRadius: 10,
}}
onChangeText={newText => onChangeSearch(newText)}
placeholder="Axtaris..."
/>
<FlatList
data={filteredData}
renderItem={renderItem}
keyExtractor={(item, index) => item.key}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
marginBottom: 75,
},
item: {
backgroundColor: '#ededed',
padding: 20,
marginVertical: 2,
marginHorizontal: 10,
borderRadius: 20,
},
title: {
fontSize: 20,
},
});
export default Sample;
P.S. newdata has about 42000 of items, and app running very slow. That is the screenshot of app
You can easily achieve that by using the initialNumToRender prop in FlatList component
How many items to render in the initial batch. This should be enough to fill the screen but not much more. Note these items will never be unmounted as part of the windowed rendering in order to improve perceived performance of scroll-to-top actions.
<FlatList
data={filteredData}
renderItem={renderItem}
keyExtractor={item => item.key}
initialNumToRender={10}
/>
Ali,
You have some features inside Flatlist in order to optimize it for example:
maxtorenderperbatch: This controls the amount of items rendered per batch, which is the next chunk of items rendered on every scroll.
Read more here
initialNumToRender: Define precise number of items that would cover the screen for every device. [Read more here][1]
Also, you can use Infinite Scroll, is very useful instead of render
the whole list you can render only amount of items, and when the user
is scrolling to the end the app load more items.
onEndReached: Called once when the scroll position gets within onEndReachedThreshold of the rendered content.
onEndReachedThreshold: How far from the end (in units of visible length of the list) the bottom edge of the list must be from the end
of the content to trigger the onEndReached callback.
Here is an example how to use it.
<FlatList
data={filteredData}
renderItem={renderItem}
keyExtractor={(item, index) => item.key}
onEndReached={ loadMoreItems }
onEndReachedThreshold={ 0.5 }
maxToRenderPerBatch={3}
initialNumToRender={5}
/>
loadMoreItems:
const loadMoreItems = ( ) => {
// Here you logic to render more items, when user scroll to the end
}

Check visibility of item at the screen

I have FlastList with data. I need to check if item from the Flastlist is seen. So if i do not see data at my screen i do not have to do anything, but if i see i have to console.log data info. And when i am scrolling i have to console.log data that is visibile. I am trying to use onViewableItemsChanged with viewabilityConfig, but when i console.log data, it returns all data from FlastList but not data that is seen. Help me please.
I will be very thankfull!
_onViewableItemsChanged = ({ viewableItems, changed }) => {
console.log("Visible items are", viewableItems.map(item => item.item.text));
};
_viewabilityConfig = {
viewAreaCoveragePercentThreshold: 100
};
//....
<FlatList
data={this.state.postData}
initialNumToRender={0}
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={({item}) => {
return (
<View style={{paddingTop: 15}}
ref={ (divElement) => { this.divElement = divElement } }
>
// data
)}
}
onViewableItemsChanged={this._onViewableItemsChanged}
viewabilityConfig={this._viewabilityConfig}
keyExtractor={item => item.id}
/>
In your comment you specified that the FlatList is inside a view and scrollview. I tried to reproduce it this way:
<View style={styles.container}>
<ScrollView style={styles.scrollView}>
<FlatList
data={DATA}
initialNumToRender={0}
renderItem={renderItem}
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
keyExtractor={item => item.id}
/>
</ScrollView>
</View>
And indeed, console.log shows all data in this case. When I removed ScrollView so that the FlatList is inside the View element (with flex:1) then it works correctly - console.log shows only visible elements. My code:
const DATA = [
{
id: '1',
title: 'First Item',
},
// ... more elements
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const renderItem = ({item}) => {
return (
<View style={{paddingTop: 15}}
ref={ (divElement) => { this.divElement = divElement } }
>
<Item title={item.title} />
</View>
)
}
const viewabilityConfig = {
viewAreaCoveragePercentThreshold: 100
};
const onViewableItemsChanged = ({ viewableItems, changed }) => {
console.log("Visible items are", viewableItems.map(item => item.item.title));
};
export default function App() {
return (
<View style={styles.container}>
<FlatList
data={DATA}
initialNumToRender={0}
renderItem={renderItem}
onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig}
keyExtractor={item => item.id}
/>
<StatusBar style="auto" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
marginHorizontal: 16,
},
title: {
fontSize: 32,
},
});

react native search and scroll to hit

I need some help implementing a search and scroll to hit in react native. Did a lot of searches and ended up in some dead ends (found some refs examples I couldn't get to work).
Tried building this snippet as a kick-off:
https://snack.expo.io/#norfeldt/searching-and-scroll-to
import React, { Component } from 'react';
import { Text, View, ScrollView, TextInput, StyleSheet } from 'react-native';
export default class App extends Component {
state = {
text: '41'
}
render() {
return (
<View style={styles.container}>
<TextInput
style={{height: 60, borderColor: 'gray', borderWidth: 1, borderRadius: 10, margin: 5, padding:30, color: 'black', }}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
/>
<ScrollView >
{[...Array(100)].map((_, i) => {return <Text style={styles.paragraph} key={i}>{i}</Text>})}
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 10,
backgroundColor: '#ecf0f1',
},
paragraph: {
margin: 10,
fontSize: 18,
fontWeight: 'bold',
textAlign: 'center',
color: '#34495e',
},
});
Any help getting started would be appreciated.
My guess would be:
You could start by binding a ref of your <ScrollView/>.
// ScrollView Ref.
<ScrollView ref={(ref) => this['ScrollView'] = ref}>
...
</ScrollView>
And each of your <Text/> components (by index).
// Text Refs.
<Text ref={(ref) => this[i] = ref} style={styles.paragraph} key={i}>{i}</Text>
You could then set a submit() function.
Said function could find the ref equal to this.state.text using a try catch statement for graceful failure in edge cases.
If found; target x and y offset could be retrieved using measure()
scrollTo() could then be called to scroll to the target component.
// Scroll To Query.
submit = () => {
try {
const { text } = this.state // Text.
const target = this[text] // Target.
// Locate Target.
target.measure((framex, framey, width, height, x, y) => {
// Scroll To Target.
this.ScrollView.scrollTo({x, y, animated: true})
})
} catch (error) {
return console.log(error)
}
}
First of all, I highly recommend you to use FlatList instead of ScrollView. There are a few reasons for this:
FlatList has much more optimized performance in comparison with ScrollView (in scroll view all items are rendered at once, regardless of the fact if they are visible on screen or not)
Moreover, the handling scrolling and rendered items is much simpler in FlatList, you do not need to know anything about x, y axis and pixels, you just work with indexes.
in order to have a comprehensive comparison between these two methods you may look at:
http://matthewsessions.com/2017/05/15/optimizing-list-render-performance.html
Now back to your question, as I said I suggest you to use FlatList, then everything will be as simple as a piece of a cake.
You can find modified example of your expo in:
https://snack.expo.io/HkMZS1SGz
The changes that you need to make in your code, include:
Instead of ScrollView use, FlatList, so change this:
<FlatList
ref={ref => {this.flatListRef = ref;}}
data={new Array(100).fill(0).map((item, index) => index)}
renderItem={({ item }) => (
<Text style={styles.paragraph}>
{item}
</Text>
)}
/>
If you are not already familiar with FlatList, you need to know, the data is added in data prop as an array (I added an array of 100 numbers), and the way it is rendered is given to FlatList as renderItemprop (I added the text with the same styling as you did).
Moreover, note that you do not need to pass ref to <Text>, because FlatList already knows about items that it contains. You just need to add a ref to the FlatList itself:
ref={ref => {this.flatListRef = ref;}}
Now when ever you want to make and scrolling, you can simple call scrollToIndex method of the FlatList, for example write a method called scrollHandler:
// Scroll to Query
scrollHandler = (itemIndex)=>{
this.flatListRef.scrollToIndex({animated: true, index: itemIndex});
}
just pay attention that,flatListRef is the name of the ref assigned to the FlatList.
now, when you want to perform scroll action, you can simply call this method. Forexample, modify your text input to:
<TextInput
style={{height: 60, borderColor: 'gray', borderWidth: 1,
borderRadius: 10, margin: 5, padding:30, color: 'black', }}
onChangeText={(text) => this.setState({text})}
value={this.state.text}
onSubmitEditing={()=>this.scrollHandler(this.state.text)}
/>
Steps
Remember every item's position with onLayout.
scrollTo() position when text input, and only if item found.
Code
const styles = StyleSheet.create({
textinput: {
borderBottomColor: 'purple',
textAlign: 'center',
borderBottomWidth: 2,
height: 40,
marginTop: 20,
},
text: {
textAlign: 'center',
fontSize: 16,
margin: 10,
}
});
export class App extends Component {
data = [];
datapos = {};
scrollref = null;
constructor(props) {
super(props);
/// make 100s example data
for (var i =0; i<100; ++i)
this.data.push(i);
this.state = {
inputvalue: '0'
}
}
render() {
return (
<View style={{flex: 1}}>
<TextInput style={styles.textinput}
value={this.state.inputvalue}
onChangeText={(text) => {
this.setState({inputvalue: text});
let y = this.datapos[+text];
y !== undefined && this.scrollref.scrollTo({ y, animated: true });
}}
/>
<ScrollView
ref={(ref) => this.scrollref = ref}
>
{
this.data.map( (data) => (
<Text style={styles.text}
key={data}
onLayout={(layout) => this.datapos[data] = layout.nativeEvent.layout.y}
>{data}</Text>
))
}
</ScrollView>
</View>
)
}
}
Result:

unexpected token when trying to declare a variable inside flatlist in react native

i'm trying to declare an variable inside the FlatList component in React Native
But i get unexpected token, when i do declare it.
const FixturesScreen = ({ navigation }) => (
<ScrollView style={styles.container}>
<FlatList
data={clubData.data.clubs}
renderItem={({ item }) => (
let fixture = item.name //unexpected token
<View>
<View>
<Text style={styles.itemTitle}>{item.name}</Text>
<ScrollView horizontal>
<Text style={styles.listItem}>{fixture}</Text>
</ScrollView>
</View>
</View>
)
}
/>
</ScrollView>
)
here is my full FixturesScreen cdoe
import React from 'react'
import { View, Text, FlatList, ScrollView, StyleSheet } from 'react-native'
import Ionicons from 'react-native-vector-icons/Ionicons'
import clubData from '../../clubData'
const styles = StyleSheet.create({
container: {
backgroundColor: '#4BABF4',
},
itemTitle: {
color: 'black',
fontSize: 20,
marginTop: 20,
marginBottom: 10,
marginLeft: 15,
},
listItem: {
color: '#FFFFFF',
fontSize: 18,
textAlign: 'center',
marginRight: 15,
marginLeft: 15,
backgroundColor: '#77BEF5',
width: 120,
paddingVertical: 10,
},
})
const CURRENTTGAMEWEEK = 30
const i = CURRENTTGAMEWEEK
const nxt1 = i + 1
const nxt2 = nxt1 + 2
const nxt3 = nxt2 + 1
const nxt4 = nxt3 + 1
const nxt5 = nxt4 + 1
// let fixture
// const team = clubData.data.clubs[0].name
// const hTeam = clubData.data.clubs[0].fixtures[0].homeTeam
// const hTeamShort = clubData.data.clubs[0].fixtures[0].homeTeamShort
// const aTeamShort = clubData.data.clubs[0].fixtures[0].awayTeamShort
//
// if (team === hTeam) // working
// fixture = aTeamShort
// else
// fixture = hTeamShort
console.log(`Now is playing ${fixture}`)
const FixturesScreen = ({ navigation }) => (
<ScrollView style={styles.container}>
<FlatList
data={clubData.data.clubs}
renderItem={({ item }) => (
let fixture = item.name
<View>
<View>
<Text style={styles.itemTitle}>{item.name}</Text>
<ScrollView horizontal>
<Text style={styles.listItem}>{fixture}</Text>
</ScrollView>
</View>
</View>
)
}
/>
</ScrollView>
)
FixturesScreen.navigationOptions = {
tabBarTestIDProps: {
testID: 'TEST_ID_HOME',
accessibilityLabel: 'TEST_ID_HOME_ACLBL',
},
tabBarLabel: 'Main',
tabBarIcon: ({ tintColor, focused }) => (
<Ionicons
name={focused ? 'ios-home' : 'ios-home-outline'}
size={26}
style={{ color: tintColor }}
/>
),
}
export default FixturesScreen
So basically what i'm trying to do is declare homeTeam, awayTeam and Fixture inside the flatlist, so i can do an if/else conditional rendering inside the flatlist. I can achieve that outside the flatlist component but it is not right, because i can not compare all objects at once.
While using arrow functions () => ('someValue') is a shortcut for () => { return 'someValue'}.
(param1, param2, …, paramN) => { statements }
(param1, param2, …, paramN) => expression
// equivalent to: (param1, param2, …, paramN) => { return expression; }
// Parentheses are optional when there's only one parameter name:
(singleParam) => { statements }
singleParam => { statements }
// A function with no parameters should be written with a pair of parentheses.
() => { statements }
// Parenthesize the body of function to return an object literal expression:
params => ({foo: bar})
So if you want to run some code before returning a value you should do like below;
const FixturesScreen = ({ navigation }) => (
<ScrollView style={styles.container}>
<FlatList
data={clubData.data.clubs}
renderItem={({ item }) => { //change to curly braces
let fixture = item.name;
// do something here
return (
<View>
<View>
<Text style={styles.itemTitle}>{item.name}</Text>
<ScrollView horizontal>
<Text style={styles.listItem}>{fixture}</Text>
</ScrollView>
</View>
</View>
);
}}
/>
</ScrollView>
)

Resources