Previously we learn how we can implement a SectionList . In this blog we will learn how to make section list expandable.
Here we'll use a flag isExpanded in data list, Initially isExpanded is set to false for each data item in the list. In each click of header section we will change the value of isExpanded.
1. Declare the data list or if you have dynamic data format the data as below
const DATA = [
{ isExpanded:false,
title: 'Main dishes',
data: ['Pizza', 'Burger', 'Risotto'],
},
{
title: 'Sides',
data: ['French Fries', 'Onion Rings', 'Fried Shrimps'],
isExpanded:false,
},
{
title: 'Drinks',
data: ['Water', 'Coke', 'Beer'],
isExpanded:false,
},
{
title: 'Desserts',
data: ['Cheese Cake', 'Ice Cream'],
isExpanded:false,
},
{
title: 'Drinks',
data: ['Water', 'Coke', 'Beer'],
isExpanded:false,
},
{
title: 'Desserts',
data: ['Cheese Cake', 'Ice Cream'],
isExpanded:false,
},
];
2. Define constructor in your class and set state
constructor(){
super()
this.state={
listData:DATA
}
}
Here DATA list which we define above assign to listData for binding list data from state. If you have dynamic data the after fetching update state variable "listData" with new data .
3. Now set-up the SectionList component , here data source we are using from the state.
<SectionList
stickySectionHeadersEnabled
sections={this.state.listData}
keyExtractor={ (item, index) => item + index}
renderItem={
({ item,section }) => <Item title={item} section={section} />
}
renderSectionHeader={
({ section }) => (
<TouchableOpacity onPress={()=>this.expand(section.title)}>
<View style={{flexDirection:"row",justifyContent:'space-between'}}>
<Text style={styles.header}>{section.title}</Text>
{(section.isExpanded)?
(<FontAwesome name="caret-up" size={15}/>)
:
(<FontAwesome name="caret-down" size={15}/>) }
</View>
</TouchableOpacity>
)
}
/>
In renderItem component we are passing section data along with item, so we can check wether isExpanded is set true or false. If it is set to false then we will return an empty View, if true then we will return the list item.
function Item({ title,section }) {
console.log("data in props #####::"+JSON.stringify(section));
if(!section.isExpanded){
return (<View/> );
}else{
return (
<View style={styles.item}>
<Text style={styles.title}>{title}Text>
View>
);
}
}
expand() function is used for expanding the list item, so on each click of header section we are going to invoke the expand() function setting up the updated value of isExpanded flag as below. And along with update the state so list can render with updated data.
expand=(title)=>{
let existing=DATA.find(item=>item.title === title)
if(existing.isExpanded === false){
existing.isExpanded=true
}else{
existing.isExpanded=false
}
this.setState({
listData:DATA
})
}
4. That's all, our expandable sectionList is done. Full source code will look like below.
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
SafeAreaView,
SectionList,
TouchableOpacity
} from 'react-native';
import {FontAwesome,MaterialCommunityIcons} from 'react-native-vector-icons';
import Constants from 'expo-constants';
import {SearchBar} from 'react-native-elements';
const DATA = [
{ isExpanded:false,
title: 'Main dishes',
data: ['Pizza', 'Burger', 'Risotto'],
},
{
title: 'Sides',
data: ['French Fries', 'Onion Rings', 'Fried Shrimps'],
isExpanded:false,
},
{
title: 'Drinks',
data: ['Water', 'Coke', 'Beer'],
isExpanded:false,
},
{
title: 'Desserts',
data: ['Cheese Cake', 'Ice Cream'],
isExpanded:false,
},
{
title: 'Drinks',
data: ['Water', 'Coke', 'Beer'],
isExpanded:false,
},
{
title: 'Desserts',
data: ['Cheese Cake', 'Ice Cream'],
isExpanded:false,
},
];
function Item({ title,section }) {
console.log("data in props #####::"+JSON.stringify(section));
if(!section.isExpanded){
return (<View/> );
}else{
return (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
}
}
export default class SectionListScreen extends Component{
constructor(){
super()
this.state={
listData:DATA
}
}
expand=(title)=>{
let existing=DATA.find(item=>item.title === title)
if(existing.isExpanded === false){
existing.isExpanded=true
}else{
existing.isExpanded=false
}
this.setState({
listData:DATA
})
}
render(){
return (
<SafeAreaView style={styles.container}>
<SectionList
stickySectionHeadersEnabled
sections={this.state.listData}
keyExtractor={
(item, index) => item + index
}
renderItem={
({ item,section }) => <Item title={item} section={section} />
}
renderSectionHeader={
({ section }) => (
<TouchableOpacity onPress={()=>this.expand(section.title)}>
<View style={{flexDirection:"row",
justifyContent:'space-between'}}>
<Text style={styles.header}>{section.title}</Text>
{(section.isExpanded)?
(<FontAwesome name="caret-up" size={15}/>)
:
(<FontAwesome name="caret-down" size={15}/>) }
</View>
</TouchableOpacity>
)
}
/>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: Constants.statusBarHeight,
marginHorizontal: 16,
},
item: {
padding: 10,
borderRadius:5,
borderWidth:1,
borderColor:"black",
marginVertical: 5,
},
header: {
fontSize: 20,
fontWeight:'bold'
},
title: {
fontSize: 16,
},
});