The Ultimate Guide to FlatList in React Native
Subramanya Chakravarthy
Posted on June 29, 2020
If you have a list of data like Facebook feed or YouTube like infinite list of videos, the most performant way to render in react-native is only by using FlatList.
Basic Usage
A basic usage of FlatList requires three props. One is data which takes an array of things like objects which is needed to render it, Second is renderItem which is a function that accepts individual item from data Array and render a component for it. And the last one is keyExtractor which is unique and is useful for performant rendering
Here's a Basic Usage of FlatList from docs
<FlatList
data={[{key: 'a'}, {key: 'b'}]}
renderItem={({item}) => <Text>{item.key}</Text>}
/>
In a real world you get data dynamically, So Let's get some dummy data from Random User API
class FlatListDemo extends Component {
state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: false,
};
componentDidMount() {
this.setState({ loading: true }, () => this.makeRemoteRequest());
}
makeRemoteRequest = async () => {
const { page, seed } = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
try {
const response = await fetch(url);
const data = await response.json();
this.setState(
{
data: page === 1 ? data.results : [...this.state.data, ...data.results],
error: data.error || null,
loading: false,
refreshing: false,
},
// () => this.searchFilterFunction("")
);
} catch (error) {
this.setState({ error, loading: false });
}
};
_renderItem = ({ item }) => (
<View
style={{
flexDirection: 'row',
flex: 1,
}}
>
<Image
style={{ width: 40, height: 40, borderRadius: 20, margin: 5 }}
source={{ uri: item.picture.thumbnail }}
resizeMethod={'resize'}
/>
<View style={{ justifyContent: 'center', marginLeft: 5 }}>
<Text>{`${item.name.first} ${item.name.last}`}</Text>
<Text>{item.email}</Text>
</View>
</View>
);
_keyExtractor = (item, index) => item.email;
render() {
return (
<FlatList
data={this.state.data}
renderItem={this._renderItem}
keyExtractor={this._keyExtractor}
/>
);
}
}
Before receiving content from the api, initially we don't have any data, For those scenarios show a Loading indicator, to do that ListEmptyComponent can be used
ListEmptyComponent={<ActivityIndicator animating size={"large"} />}
There are no separators between each contact, so let's add them by using ItemSeparatorComponent.
ItemSeparatorComponent={
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
}
Let's add a search box, so we can find contacts, to accomplish this we will use ListHeaderComponent
ListHeaderComponent={this.renderHeader}
Before we add renderHeader method add the following to state
filteredData: [],
error: null,
refreshing: false
And for tracking search input value add this.inputSearch = ""
to constructor outside of state and undo the searchFilterFunction comment in makeRemoteRequest function and change data prop in FlatList to this.state.filteredData
And here's the renderHeader function, which contains TextInput where it has a searchFilterFunction which returns list based on input
renderHeader = () => {
return (
<View style={{ alignItems: 'center', padding: 10, backgroundColor: '#fff' }}>
<TextInput
style={{
flex: 1,
width: width / 1.1,
borderWidth: 1,
borderRadius: 15,
}}
underlineColorAndroid={'transparent'}
placeholder={'Search'}
onChangeText={text => this.searchFilterFunction(text)}
value={this.inputSearch}
/>
{this.inputSearch.length > 0 && <Text>{this.state.filteredData.length} items found</Text>}
</View>
);
};
And here's the searchFilterFunction where it combines first and last name and using filter function from JavaScript to return sorted data
searchFilterFunction = text => {
this.inputSearch = text;
const newData = this.state.data.filter(item => {
const itemData = `${item.name.first.toUpperCase()} ${item.name.last.toUpperCase()}`;
const textData = text.toUpperCase();
return itemData.indexOf(textData) > -1;
});
this.setState({ filteredData: newData });
};
Now the search is working with the available data, let's request for more data when user scrolls to the end. Let's add a prop onEndReachedThreshold={0.9}
which takes value from 0 to 1. I have give 0.9 which means onEndReached={this.handleonEndReached}
prop is called when list is scrolled to 90%. When list reaches to 90% let's make a request to get more data.
handleOnEndReached = info => {
if (info.distanceFromEnd >= -10) {
this.inputSearch.length === 0 &&
!this.state.loading &&
this.setState(
(state, props) => {
return { loading: true, page: state.page + 1 };
},
() => this.makeRemoteRequest(),
);
}
};
I have added few checks before making new network request
- check distance from end
- check search input is 0 or not
- check did a request is in progress via state.loading
Let's add a scrolling indicator when new request is made, it can be done by adding ListFooterComponent prop to FlatList
ListFooterComponent={this.renderFooter}
renderFooter = () => {
if (this.state.loading && this.state.data.length !== 0) {
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE",
alignItems: "center",
justifyContent: "center"
}}
>
<ActivityIndicator animating size={"large"} />
</View>
);
}
return null;
};
Have you ever used pull down to refresh or update data in some popular apps like facebook, you can add same functionality by using two props refreshing
which takes state and shows loading indicator and onRefresh
which will be called when you pull down.
// FlatList props
refreshing={this.state.refreshing}
onRefresh={this.handleRefresh}
//handleRefresh function
handleRefresh = () => {
this.setState({ refreshing: true, seed: Math.random(), page: 1 }, () =>
this.makeRemoteRequest()
);
};
Now you can use pull to refresh data and new data will be loaded when scrolled to end but there would be lag while scrolling. In order to fix that let's control initial number of items to render and maximum render per batch
initialNumToRender={8}
maxToRenderPerBatch={2}
Here's the Code
Posted on June 29, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 29, 2024
November 29, 2024
November 29, 2024
November 28, 2024