Sudhir Kumar Ojha

@sudhirKr

How to highlight and multi-select items in a FlatList component- React Native

You’ve probably heard about Flatlist component if you are working in React native and handling lists of various client data and details either from the API or form fields. Basically, Flatlist is a core component designed for efficient display of vertically scrolling lists of changing data. It is a component which came into existence in React native after the 0.43 version, it replaced the ListView component and enhanced the ability of developers to deal with lists more easily.

What is Flatlist and How can we use it for displaying dynamic or static lists?

FlatList is a simple component introduced to remove the limitations of the ListView component. The basic props required to handle a Flatlist are data and renderItem. For simplicity, data is just a plain array, whereas renderItem renders this data array and provides the developer the metadata like index etc.

<FlatList   
data={this.state.dataSource}
renderItem={({item}) => <Text>{item.key}</Text>
/>

Using FlatList to display a list of Items:

If you are new to React Native, I would recommend you to go through this article here and try to learn the basic structure and come back again to understand this in a better way.

Next Step:

Here is the step by step guide on how to use FlatList to fetch data from a dummy API and use it to display a list of items with an image.

The first step is to import the Flatlist component from the react-native library.

import { FlatList } from "react-native";

Now as we have imported the component, it’s time to use this component in the render function.

<FlatList 
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={item => this.renderItem(item)}
keyExtractor={item => item.id.toString()}
/>

Points to Ponder:

  1. data prop takes data from {this.state.dataSource} and provides us with an array of objects.
  2. ItemSeparatorComponent helps in separating the list items when displayed in a list.
  3. the renderItem method is the most important part of a FlatList as it renders the array data in a manner that it can be developed as per data & design requirements.
  4. As we are dealing with an array here keyExtractor prop provides requires a unique key to handle the list item separately as required.
  5. There is one more essential prop called extraData which I will describe later in this article.

Now you have the basic understanding of how a Flatlist work. It’s time to implement this knowledge and logic in code. As we are using data from API I would be adding an indicator to display a loader till the data loads in the backend. It’s time to fetch the data and render it in a list. If you want to know more about fetching the data from the API you can read this article.

constructor(props) {
super(props);
this.state = {
loading: false,
dataSource: [],
};
}
componentDidMount() {this.fetchData();}
fetchData = () => {this.setState({loading: true});
fetch("https://jsonplaceholder.typicode.com/photos")
.then(response => response.json())
.then(responseJson => {
responseJson = responseJson.map(item => {
item.isSelect = false;
item.selectedClass = styles.list;
return item;
});
this.setState({
loading: false,
dataSource: responseJson
});
}).catch(error => {this.setState({loading: false});
});
};
renderItem = data => 
<TouchableOpacity style={[styles.list, data.item.selectedClass]
onPress={() => this.selectItem(data)}>
<Image source={{ uri: data.item.thumbnailUrl }}
style={{ width: 40, height: 40, margin: 6 }}
/>
<Text style={styles.lightText}{data.item.title.charAt(0).toUpperCase() + data.item.title.slice(1)} </Text>
</TouchableOpacity>
List rendered without ItemSeparator

So far we have handled the data and renderItem method and now it’s time to drill into remaining ones.

FlatListItemSeparator = () => <View style={styles.line} />;
List rendered with ItemSeparator

You can provide the styling as per your design and change this style class. If you want to use my version of styling, I would share the code at the end of this article and you can get it from there. Now The key extractor method is basically for the unique id for the items and it’s better to use the exact code above to handle any warnings.

Now comes the fun part for what you have been waiting till now:

Any then and now, you must have come through a basic requirement where you need to select multiple items in a list and highlight it. Especially in marketing apps or todo list where you need to choose from hundreds of items in a list. Here is the code for selecting an item among various other items.

selectItem = data => {
data.item.isSelect = !data.item.isSelect;
data.item.selectedClass = data.item.isSelect
? styles.selected: styles.list;

const index = this.state.dataSource.findIndex(
item => data.item.id === item.id
);
this.state.dataSource[index] = data.item;
this.setState({
dataSource: this.state.dataSource
});
};
Selected Items in a list

What we have here is a screen with selected items highlighted in a bright color whereas the other items in the list are set to have default styling. In order to understand the logic more clearly let’s look into the fetch component

.then(responseJson => {
responseJson = responseJson.map(item => {
item.isSelect = false;
item.selectedClass = styles.list;
return item;
});

In here we have assigned item.isSelect as false and selectedClass is assigned a default style class called list, the reason behind doing this is, Now each and every item in our list will be having these two props inside them which can be used to fetch the item uniquely and manipulate it. Now in our selectItem() function, we added a rendering condition as follows:

selectItem = data => {
data.item.isSelect = !data.item.isSelect;
data.item.selectedClass = data.item.isSelect
? styles.selected: styles.list;

Next, when we click on any item in our FlatList the selectItem function gets rendered and it changes the styling of that item to a highlighted class such as selected class here and rest of the list items have default styling from the class list.

Here comes the important part:

FlatList has a prop called extraData and what it does basically is it re-renders the FlatList whenever there is any change using state. This feature re-renders our selectItem and renderItem function and highlights the selected items otherwise only one item will be selected as the FlatList is rendered at the beginning of the component loading and remains in the same state even if the state and data change state.

extraData

A marker property for telling the list to re-render (since it implements PureComponent).

extraData={this.state}

All you have to do is add it in the FlatList component and we are done with highlighting multiple items in a list.

Hope this article is helpful to you and enhances your ability to deal with this requirement easily. I am sharing full code snippet here if you want to use it as an example.

Screen with selected and unselected items

Full code here:

import React from "react";
import{StyleSheet,View,ActivityIndicator,FlatList,Text,TouchableOpacity,Image} from "react-native";
import { Icon } from "react-native-elements";
import { enText } from "../lang/en"
export default class Store extends React.Component { 
constructor(props) {
super(props)
this.state = {
loading: false,
dataSource: [],
};
}
componentDidMount() {this.fetchData();}
fetchData = () => {this.setState({loading: true});
fetch("https://jsonplaceholder.typicode.com/photos")
.then(response => response.json())
.then(responseJson => {
responseJson = responseJson.map(item => {
item.isSelect = false;
item.selectedClass = styles.list;
return item;
});
this.setState({
loading: false,
dataSource: responseJson,
});
}).catch(error => {this.setState({loading: false});
});
};
FlatListItemSeparator = () => <View style={styles.line} />;
selectItem = data => {
data.item.isSelect = !data.item.isSelect;
data.item.selectedClass = data.item.isSelect?
styles.selected: styles.list;
const index = this.state.dataSource.findIndex(
item => data.item.id === item.id
);
this.state.dataSource[index] = data.item;
this.setState({
dataSource: this.state.dataSource,
});
};
goToStore = () =>this.props.navigation.navigate("Expenses", {selected: this.state.selected,});
renderItem = data =>
<TouchableOpacity
style={[styles.list, data.item.selectedClass]}
onPress={() => this.selectItem(data)}
>
<Image
source={{ uri: data.item.thumbnailUrl }}
style={{ width: 40, height: 40, margin: 6 }}
/>
<Text style={styles.lightText}> {data.item.title.charAt(0).toUpperCase() + data.item.title.slice(1)} </Text>
</TouchableOpacity>
render() {
const itemNumber = this.state.dataSource.filter(item => item.isSelect).length;
if (this.state.loading) {return (
<View style={styles.loader}>
<ActivityIndicator size="large" color="purple" />
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.title}>{enText.productsAvailable}</Text>
<FlatList
data={this.state.dataSource}
ItemSeparatorComponent={this.FlatListItemSeparator}
renderItem={item => this.renderItem(item)}
keyExtractor={item => item.id.toString()}
extraData={this.state}
/>
<View style={styles.numberBox}>
<Text style={styles.number}>{itemNumber}</Text>
</View>
<TouchableOpacity style={styles.icon}>
<View>
<Icon
raised
name="shopping-cart"
type="font-awesome"
color="#e3e3e3"
size={30}
onPress={() => this.goToStore()}
containerStyle={{ backgroundColor: "#FA7B5F" }}
/>
</View>
</TouchableOpacity>
</View>
);}}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#192338",
paddingVertical: 50,
position: "relative"
},
title: {
fontSize: 20,
color: "#fff",
textAlign: "center",
marginBottom: 10
},
loader: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff"
},
list: {
paddingVertical: 5,
margin: 3,
flexDirection: "row",
backgroundColor: "#192338",
justifyContent: "flex-start",
alignItems: "center",
zIndex: -1
},
lightText: {
color: "#f7f7f7",
width: 200,
paddingLeft: 15,
fontSize: 12
},
line: {
height: 0.5,
width: "100%",
backgroundColor:"rgba(255,255,255,0.5)"
},
icon: {
position: "absolute",
bottom: 20,
width: "100%",
left: 290,
zIndex: 1
},
numberBox: {
position: "absolute",
bottom: 75,
width: 30,
height: 30,
borderRadius: 15,
left: 330,
zIndex: 3,
backgroundColor: "#e3e3e3",
justifyContent: "center",
alignItems: "center"
},
number: {fontSize: 14,color: "#000"},
selected: {backgroundColor: "#FA7B5F"},
});
If you liked this article. Press the claps icon as many as times you can. If you have any projects in mind and are looking for a developer for it either mobile or web. Kindly get in touch with me on my Instagram channel. Together we can create this world a better place.
Know more about me and my projects through my github repository.You can also checkout my other articles published in medium.

Thanks, Keep learning and Sharing :-)

Topics of interest

More Related Stories