FlatList inside SectionList - react-native

I have screen with 3 different Lists, all lists have some custom header/footer and Lists contain big amount of data, so my question is - is there any performance issue with FlatList inside of SectionList?
This is rough example what i want to do
const App = () => {
const renderItem = ({item, section, index}) => {
switch (section.type) {
case 'LIST_1':
return (
<View>
<Text>Some custom set of components</Text>
<FlatList
data={section.items}
renderItem={({item}) => (
<View
style={{
padding: 20,
margin: 10,
backgroundColor: 'blue',
}}>
<Text>{item.title}</Text>
</View>
)}
keyExtractor={item => item.id}
/>
</View>
);
case 'LIST_2':
return (
<View>
<Text>Some custom set of components</Text>
<FlatList
data={section.items}
renderItem={({item}) => (
<View
style={{
padding: 20,
margin: 10,
backgroundColor: 'red',
}}>
<Text>{item.count}</Text>
</View>
)}
keyExtractor={item => item.id}
/>
</View>
);
case 'LIST_3':
return (
<View>
<Text>Some custom set of components</Text>
<FlatList
data={section.items}
renderItem={({item}) => (
<View
style={{
padding: 20,
margin: 10,
backgroundColor: 'blue',
}}>
<Text>{item.score}</Text>
</View>
)}
keyExtractor={item => item.id}
/>
</View>
);
}
};
const sections = [
{
type: 'LIST_1',
data: [1],
items: Array.from(Array(50)).map((el, i) => ({
id: i + 1,
title: i + 1,
})),
},
{
type: 'LIST_2',
data: [2],
items: Array.from(Array(50)).map((el, i) => ({
id: i + 1,
count: i + 1,
})),
},
{
type: 'LIST_3',
data: [3],
items: Array.from(Array(50)).map((el, i) => ({
id: i + 1,
score: i + 1,
})),
},
];
return (
<SafeAreaView>
<SectionList
sections={sections}
keyExtractor={item => item}
renderItem={renderItem}
/>
</SafeAreaView>
);
};
If this is not optimal solution and ScrollView takes a lot of time to render, Can you guide me what is better?

Your example can be achieved simply by using SectionList.
// Here we define section objects. I arbitrarily added title, footer and style.
const DATA = [
{
title: 'Section 1 header',
footer: 'Section 1 footer',
data: [...Array(3).keys()],
style: { backgroundColor: 'red' },
},
{
title: 'Section 2 header',
footer: 'Section 2 footer',
data: [...Array(3).keys()],
style: { backgroundColor: 'blue' },
},
{
title: 'Section 3 header',
footer: 'Section 3 footer',
data: [...Array(3).keys()],
style: { backgroundColor: 'yellow' },
},
]
// renderItem gets passed an object with item, section, index and separator keys. I'm using item and section to access my own section style.
const Item = ({ item, sectionStyle }) => (
<View style={[styles.item, sectionStyle]}>
<Text style={styles.title}>{item}</Text>
</View>
)
// Use renderSectionHeader and renderSectionFooter props to add, respectively, header and footer.
const App = () => (
<SafeAreaView style={styles.container}>
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item, section }) => <Item item={item} sectionStyle={section.style} />}
renderSectionHeader={({ section: { title } }) => <Text style={styles.header}>{title}</Text>}
renderSectionFooter={({ section: { footer } }) => <Text style={styles.header}>{footer}</Text>}
/>
</SafeAreaView>
)
And to answer your original question, you might run into performance issues with react-natives list components, it depends on many factors, including your data and rendered components.
You can read more on the topic here:
https://reactnative.dev/docs/optimizing-flatlist-configuration

Related

last FlatList content only display 10% of the item

here i use flatlist grid inside scrollview, the problem is the last row of the grid didn't display the full height of the content, i don't know where the problem is.
note: i use native-base component
const [data, setData] = useState([
{ id: 1, title: "DA’s Backyard" },
{ id: 2, title: "KC’s Backyard" },
{ id: 3, title: "LA’s Backyard" },
{ id: 4, title: "KP’s Backyard" },
{ id: 5, title: "LA’s Backyard" },
{ id: 6, title: "KP’s Backyard" },
]);
<ScrollView
showsVerticalScrollIndicator={false}
contentContainerStyle={{ width: "100%", alignItems: "center" }}
>
<Stack flexWrap={"wrap"} justifyContent="center">
<SelectBox />
<FlatList
mt={"4"}
showsVerticalScrollIndicator={false}
data={data}
keyExtractor={(item, index) => index}
numColumns={2}
renderItem={(data, index) => {
return <PortfolioCard />;
}}
/>
</Stack>
</ScrollView>
Add this please to flat list
<FlatList
mt={"4"}
showsVerticalScrollIndicator={false}
data={data}
keyExtractor={(item, index) => index}
numColumns={2}
renderItem={(data, index) => {
return <PortfolioCard />;
}}
contentContainerStyle={{paddingBottom:50}} // add this
/>

Sticky header on SectionList ReactNative

I need to create a screen Catalog(Categories and Products).
I'm using SectionList from React Native in order to achive this.
I need to make that Categories component stick on the top when you scroll product lists.
Is there any library that could help me with this Catalog screen ?
Please look at the image here..
import React from "react";
import { View, StyleSheet, SectionList } from "react-native";
import Text from "../Text";
const DATA = [
{
title: "Main dishes",
data: ["Pizza", "Burger", "Risotto"],
},
{
title: "Sides",
data: ["French Fries", "Onion Rings", "Fried Shrimps"],
},
{
title: "Drinks",
data: ["Water", "Coke", "Beer"],
},
{
title: "Desserts",
data: ["Cheese Cake", "Ice Cream"],
},
];
const TabCategories = () => (
<View>
<Text>Horizontal list of categories</Text>
</View>
);
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const TestSectionList = (props) => {
return (
<View style={styles.container}>
<Text style={styles.SRC}>Some React Component</Text>
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Item title={item} />}
renderSectionHeader={({ section: { title } }) => (
<Text style={styles.header}>{title}</Text>
)}
StickyHeaderComponent={TabCategories}
/>
</View>
);
};
const styles = StyleSheet.create({
container: {},
SRC: {
fontWeight: "bold",
borderWidth: 1,
borderColor: "#fff",
padding: 10,
},
item: {
padding: 30,
},
header: {
fontWeight: "bold",
fontSize: 20,
},
});
export default TestSectionList;
stickySectionHeadersEnabled
Makes section headers stick to the top of the screen until the next one pushes it up
ListHeaderComponent
Rendered at the very beginning of the list
renderSectionHeader
Rendered at the top of each SECTION
I think this should do:
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Item title={item} />}
ListHeaderComponent={({ section: { title } }) => (
<Text style={styles.header}>{title}</Text>
)}
renderSectionHeader={TabCategories}
stickySectionHeadersEnabled
/>
You can try this library react-native-tabs-section-list
https://github.com/bogoslavskiy/react-native-tabs-section-list
If you are talking about react-native-section-list, it inherits ScrollView props, you can check in the docs, in props section, so it has stickyHeaderComponent prop which should be exactly what you want.

Display custom image and link to a sectionList item on react native

I'm a newbie on react-native.
I want to build personalized data from array to populate sectionLists.
My actual code, extracted from react-native docs is
import React from "react";
import { StyleSheet, Text, View, SafeAreaView, SectionList, StatusBar } from "react-native";
const DATA = [
{
title: "Main dishes",
data: ["Pizza", "Burger", "Risotto"],
},
{
title: "Sides",
data: ["French Fries", "Onion Rings", "Fried Shrimps"]
},
{
title: "Drinks",
data: ["Water", "Coke", "Beer"]
},
{
title: "Desserts",
data: ["Cheese Cake", "Ice Cream"]
}
];
const Item = ({ title }) => (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
const App = () => (
<SafeAreaView style={styles.container}>
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <Item title={item} />}
renderSectionHeader={({ section: { title } }) => (
<Text style={styles.header}>{title}</Text>
)}
/>
</SafeAreaView>
);
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: StatusBar.currentHeight,
marginHorizontal: 16
},
item: {
backgroundColor: "#f9c2ff",
padding: 20,
marginVertical: 8
},
header: {
fontSize: 32,
backgroundColor: "#fff"
},
title: {
fontSize: 24
}
});
export default App;
Here, we have only 2 dimensions to data array. I want to personalize that DATA array sending an image source, and a link to a screen on itemclick, for example.
I mean, I have actually title, data on DATA array. I want to add image and link columns to this DATA array and I want to have a solution to parse it in order to associate the link to props and display an image by its source content on image field.
Could you please tell me how to present that for the array and render it ?
Thanks.
EDIT :
I want to add 2 columns image and link on the DATA array like that :
const DATA = [
{
title: "Main dishes",
data: ["Pizza", "Burger", "Risotto"],
image: [require('../assets/Aquarium.png'), require('../assets/Aquarium.png'), require('../assets/Aquarium.png')]
link: ["Pizza", "Burger", "Risotto"]
},
{
title: "Sides",
data: ["French Fries", "Onion Rings", "Fried Shrimps"]
image: [require('../assets/Aquarium.png'), require('../assets/Aquarium.png'), require('../assets/Aquarium.png')]
link: ["FrenchFries", "OnionRings", "FriedShrimps"]
},
{
title: "Drinks",
data: ["Water", "Coke", "Beer"]
image: [require('../assets/Aquarium.png'), require('../assets/Aquarium.png'), require('../assets/Aquarium.png')],
link: ["Water", "Coke", "Beer"]
},
{
title: "Desserts",
data: ["Cheese Cake", "Ice Cream"]
image: [require('../assets/Aquarium.png'), require('../assets/Aquarium.png')]
link: ["CheeseCake", "IceCream"]
}
];
And I want to change my code like this to present image and link to a stackscreen name but it gives me errors.
return (
<SafeAreaView style={styles.container}>
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) =>
<TouchableWithoutFeedback
activeOpacity={0.4}
onPress={ () => navigation.navigate(item.link)}>
<View style={{flexDirection: 'row', textAlign: 'left', fontSize: 15, backgroundColor:'transparent', marginTop: 15, marginBottom: 15}}>
<Image source={item.image}/>
<Item title={item.title} />
</View>
</TouchableWithoutFeedback>
}
renderSectionHeader={({ section: { title } }) => (
<View>
<Text style={styles.header}>{title}</Text>
</View>
)}
/>
</SafeAreaView>
);
Could you please help me ?

Remove horizontal line from last item in react-native

I have a map which loop all items in the products object and output each new items, What i want is to remove the red line under the "product 003", which is the last item.
Image
const App = () => (
<View style={styles.container}>
{products.map((product, index) => (
<ShoppingList key={index} title={product.title} price={product.price} />
))}
</View>
);
const ShoppingList = props => {
return (
<View style={styles.line}>
<Text>{props.title}</Text>
<Text>{props.price}</Text>
</View>
);
};
const products = [
{
title: 'Product 001',
price: '100000',
},
{
title: 'Product 002',
price: '20000',
},
{
title: 'Product 003',
price: '10000',
},
];
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 24,
},
line: {
padding: 4,
borderBottomColor: 'red',
borderBottomWidth: StyleSheet.hairlineWidth,
},
});
Either use the index provided in the .map loop
{products.map((product, index) => (
(index === products.length) ? <ItemWithoutHorizontalLine /> : <ItemWithHorizontalLine />
))}
or use a VirtualizedList (or any of the child components of VirtualizedList e.g., FlatList) and provide a ItemSeparatorComponent
<FlatList
...
ItemSeparatorComponent={this.renderSeparator}
</FlatList>

How to Update state in return()?

I want to change the state when I get id of 5/6 or 7, after TouchableOpacity. Because I want to show the "Other Things" button one time whether id is 5/6 or 7.
export function HomePage({ navigation }) {
const [parentServiceCheck, setParentServiceCheck] = useState({ check: '0' });
return (
<View>
<FlatList numColumns={2} contentContainerStyle={{ margin: 7, flex: 1 }} keyExtractor={(item, index) => item.id} data={allService} renderItem={itemData => (
<View style={{ flexDirection: 'column', flex: 1 }}>
{
(itemData.item.id == 5 || itemData.item.id == 6 || itemData.item.id == 7) && parentServiceCheck.check == '0' ?
<TouchableOpacity onPress={() => pressHandler(itemData.item.id)} style={{ margin: 7 }}>
<Card titlebtn="Other Things" src={itemData.item.service_image} sty={{ height: 180, }} />
</TouchableOpacity>
// Now here I want to setParentServiceChildCheck({check: '1'}) when itemData.item.id has 5/6/7
:
<TouchableOpacity onPress={() => pressHandler(itemData.item.id)} style={{ margin: 7 }}>
<Card titlebtn={itemData.item.service_name} src={itemData.item.service_image} sty={{ height: 180, }} />
</TouchableOpacity>
}
</View>
)}
/>
</View>
)
}
You can't really update or execute any JS code like that. I would suggest to go through few training to learn about how things work in React JSX.
For now, if you want to update the state then you should be good to use it in useEffect as shown below
export default function HomePage({ navigation }) {
const [parentServiceCheck, setParentServiceCheck] = useState({ check: '0' });
useEffect(() => {
const hasValidId = allService.find(_hasValidId);
// This will get called only once after first render
// This will execute only when id's are 5, 6 or 7 which is your requirement
if (hasValidId) {
setParentServiceChildCheck({ check: '1' });
}
}, []);
const _hasValidId = itemData => {
const itemId = itemData?.item?.id;
const validItemIds = [5, 6, 7];
return validItemIds.includes(itemId);
};
const _renderItem = itemData => {
const itemId = itemData?.item?.id;
let content = null;
if (_hasValidId(itemData)) {
if (parentServiceCheck.check === '0') {
content = (
<TouchableOpacity onPress={() => pressHandler(itemId)} style={{ margin: 7 }}>
<Card titlebtn="Other Things" src={itemData.item.service_image} sty={{ height: 180 }} />
</TouchableOpacity>
);
} else {
content = (
<TouchableOpacity onPress={() => pressHandler(itemId)} style={{ margin: 7 }}>
<Card
titlebtn={itemData.item.service_name}
src={itemData.item.service_image}
sty={{ height: 180 }}
/>
</TouchableOpacity>
);
}
}
return <View style={{ flexDirection: 'column', flex: 1 }}>{content}</View>;
};
return (
<View>
<FlatList
numColumns={2}
contentContainerStyle={{ margin: 7, flex: 1 }}
keyExtractor={(item, index) => item.id}
data={allService}
renderItem={_renderItem}
/>
</View>
);
}
FYI, you might need to change few lines as the whole component code is not shared and I don't know about your business logic.
You should never update state in return / render function. It will make infinite rendering loop.