Sticky header on SectionList ReactNative - react-native

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.

Related

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 ?

Add a button "see more" in FlatList?

I use flatList to make a list of elements. I would like to show 15 elements and then add a button "see more" to show the next 15 etc.
I was about tu use this tutorial : https://aboutreact.com/react-native-flatlist-pagination-to-load-more-data-dynamically-infinite-list/
But I don't need to use fetch, I already have set up the data (state.listData) and in fact, I'm a little lost on how to adapt it...
I thought that maybe anyone could help me a little.
Thanks a lot
this.state = {
selectedId: '',
setSelectedId:'',
listData:''
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
render() {
return (
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.listData}
renderItem={this.renderItem}
maxToRenderPerBatch={15}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
);
};
}
I just tried this thanks to #Pramod 's answer :
const Item = ({ item, onPress, style }) => (
<TouchableOpacity onPress={onPress} style={[styles.flightsListitem, style]}>
<Text style={styles.h4}>{item.id}</Text>
</TouchableOpacity>
);
export default class FlightsList extends Component {
constructor(props) {
super(props);
this.state = {
selectedId: '',
setSelectedId:'',
listData:'',
page:1,
perPage:2,
loadMoreVisible:true,
displayArray:[]
}
};
renderItem = ({ item }) => {
const backgroundColor = item.id === this.selectedId ? "transparent" : "fff";
return (
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Item
item={item}
onPress={() => this.props.navigation.navigate('UpdateTripsForm')}
style={{ backgroundColor }}
/>
<Image source={require("../../assets/images/arrow.png")} style={{width: 15, height:15, justifyContent: 'center'}}/>
</View>
);
};
initListData = async () => {
let list = await getFlights(0);
if (list) {
this.setState({
listData: list
});
}
};
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.listData.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.listData)
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
async UNSAFE_componentWillMount() {
this.initListData();
}
render() {
return (
<ImageBackground
source={require("../../assets/images/background.jpg")}
style={styles.backgroundImage}
>
<Header
backgroundImage={require("../../assets/images/bg-header.png")}
backgroundImageStyle={{
resizeMode: "stretch",
}}
centerComponent={{
text: i18n.t("mytrips.title"),
style: styles.headerComponentStyle,
}}
containerStyle={[styles.headerContainerStyle, { marginBottom: 0 }]}
statusBarProps={{ barStyle: "light-content" }}
/>
<SafeAreaView style={styles.container}>
<FlatList
data={this.state.displayArray}
renderItem={this.renderItem}
keyExtractor={(item) => item.id}
extraData={this.selectedId}
/>
{this.state.loadMoreVisible == true?
<Button style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}}
title = 'load more'
onPress={()=>{this.loadMore()}}>
</Button>:null}
<TouchableOpacity
style={styles.touchable2}
onPress={() => this.props.navigation.goBack()}
>
<View style={styles.view2}>
<Text style={styles.textimg2}>
{i18n.t("tripsform.action.back")}
</Text>
</View>
<Image
source={require("../../assets/images/btn-background.png")}
style={styles.tripsimg2}
/>
</TouchableOpacity>
</SafeAreaView>
</ImageBackground>
);
};
}
the flatlist is not displayed : I get :
You can user pagination method with per page limit so that you can have granular control
Load the array per page when component mount
On every click increase the per page and based on per page update data of your flat list
And also put a flag which will check when the data has ended which will help to hide the load more button when data ends
Working example: https://snack.expo.io/#msbot01/suspicious-orange
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
SafeAreaView,
SectionList,
Switch,
FlatList
} from 'react-native';
import Constants from 'expo-constants';
import Icon from 'react-native-vector-icons/FontAwesome';
import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
// or any pure javascript modules available in npm
import { Card } from 'react-native-paper';
export default class App extends Component<Props> {
constructor(props) {
super(props);
this.state = {
page:1,
perPage:2,
loadMoreVisible:true,
DATA: [{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fourth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'fifth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29sd72',
title: 'sixth Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29dr72',
title: 'seventh Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d7w2',
title: 'Eight Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29ad72',
title: 'Nineth Item',
},
{
id: '58694a0f-3da1-471f-bd96-14557d1e29d72',
title: 'Tenth Item',
}],
displayArray:[]
}
}
componentDidMount(){
this.setNewData()
// console.log(tempArray)
}
setNewData(){
var tempArray=[]
if(this.state.DATA.length == this.state.displayArray.length){
this.setState({
loadMoreVisible:false
})
}else{
for(var i=0; i<(this.state.page*this.state.perPage); i++){
tempArray.push(this.state.DATA[i])
}
this.setState({
displayArray: tempArray,
loadMoreVisible:true
})
}
}
loadMore(){
this.setState({
page: this.state.page+1
},()=>{
this.setNewData()
})
}
render() {
return (
<View style={{ flex: 1 }}>
<FlatList
data={this.state.displayArray}
renderItem={({item})=>
<View style={{flexDirection:'row'}}>
<Text style={{fontSize:20}}>{item.title} </Text>
</View>
}
keyExtractor={item => item.id}
/>
{this.state.loadMoreVisible == true?
<View style={{width:'100%', height:10, backgroundColor:'green', justifyContent:'center', alignItems:'center'}} onClick={()=>{this.loadMore()}}>Load more</View>:null
}
</View>
);
}
}
Set data in state (already done ==> this.state.listData)
Set counter in state (initialize with 1)
Set 15 first elements in state (you can name it "renderedData" or something like that) and then increase cuonter to 1
Add a function that increases the "renderedData" by 15 items by increasing the counter by one
Add Footer component to the list which will call the function you created in stage 3
To take only 15( or 30/45/60 etc..) items from the list you can do something like this:
this.setState({ renderedItem: listData.slice(0, counter*15) })

How to add icons to tabs in react-native-tab-view

I am working with react-native-tab-view and I can just put text on tabs but I want icons. There are some answers on GitHub but they are unclear. If you know anything it would be perfect!
1. Get an imported icon library:-
import Icon from 'react-native-vector-icons/AwesomeFont'
2. Create a method for rendering the icons depending on route using props:-
const getTabBarIcon = (props) => {
const {route} = props
if (route.key === 'Search') {
return <Icon name='search' size={30} color={'red'}/>
} else {
return <Icon name='circle' size={30} color={'red'}/>
}
}
3. Render TabView with a rendered TabBar prop calling back to getTabBarIcon:-
export default class App extends React.Component {
state = {
index: 0,
routes: [
{key: 'Home', title: 'Hello'},
{key: 'Search', title: 'Second'}
],
}
render() {
return (
<TabView
navigationState={this.state}
renderScene={SceneMap({
Home: FirstRoute,
Search: SearchScreen,
})}
onIndexChange={index => this.setState({index})}
initialLayout={{height: 100, width: Dimensions.get('window').width}}
renderTabBar={props =>
<TabBar
{...props}
indicatorStyle={{backgroundColor: 'red'}}
renderIcon={
props => getTabBarIcon(props)
}
tabStyle={styles.bubble}
labelStyle={styles.noLabel}
/>
}
tabBarPosition={'bottom'}
/>
);
}
}
4. You can style the TabBar with anything (here the label is hidden to use icon only tabs)
const styles = StyleSheet.create({
scene: {
flex: 1,
},
noLabel: {
display: 'none',
height: 0
},
bubble: {
backgroundColor: 'lime',
paddingHorizontal: 18,
paddingVertical: 12,
borderRadius: 10
},
})
react-native
You have to override renderHeader method and define in TabBar your own render label method:
renderHeader = (props) => (
<TabBar
style={styles.tabBar}
{...props}
renderLabel={({ route, focused }) => (
<View style={styles.tabBarTitleContainer}>
/* HERE ADD IMAGE / ICON */
</View>
)}
renderIndicator={this.renderIndicator}
/>
);
I had the same problem. I solved it as follows by creating a "_renderTabBar" function and passing as props to the renderTabBar method of the TabView component and in this function I put the component "image" as my icon, I hope it helps.
A print:
_renderTabBar = props => {
const inputRange = props.navigationState.routes.map((x, i) => i);
return (
<View style={styles.tabBar}>
{props.navigationState.routes.map((route, i) => {
const color = props.position.interpolate({
inputRange,
outputRange: inputRange.map(
inputIndex => (inputIndex === i ? 'red' : 'cyan')
),
});
return (
<TouchableOpacity
style={[styles.tabItem, {backgroundColor: '#FFF' }]}
onPress={() => this.setState({ index: i })}>
<Image
style={styles.iconTab}
source={{uri: 'https://www.gstatic.com/images/branding/product/2x/google_plus_48dp.png'}}
/>
<Animated.Text style={{ color }}>{route.title}</Animated.Text>
</TouchableOpacity>
);
})}
</View>
);
};
Here you render
render() {
return (
<TabView
navigationState={this.state}
renderScene={this._renderScene}
renderTabBar={this._renderTabBar}
onIndexChange={index => this.setState({ index })}
/>
);
Code complete: https://snack.expo.io/#brunoaraujo/react-native-tab-view-custom-tabbar

React Native FlatList won't render

I'm trying to implement a FlatList in my React Native application, but no matter what I try the darn thing just won't render!
It's intended to be a horizontal list of images that a user can swipe through, but at this point I'd be happy to just get a list of text going.
Here's the relevant code. I can confirm that every render function is called.
render() {
const cameraScreenContent = this.state.hasAllPermissions;
const view = this.state.showGallery ? this.renderGallery() : this.renderCamera();
return (<View style={styles.container}>{view}</View>);
}
renderGallery() { //onPress={() => this.setState({showGallery: !this.state.showGallery})}
return (
<GalleryView style={styles.overlaycontainer} onPress={this.toggleView.bind(this)}>
</GalleryView>
);
}
render() {
console.log("LOG: GalleryView render function called");
console.log("FlatList data: " + this.state.testData.toString());
return(
<FlatList
data={this.state.testData}
keyExtractor={this._keyExtractor}
style={styles.container}
renderItem={
({item, index}) => {
this._renderImageView(item, index);
}
}
/>
);
}
}
_keyExtractor = (item, index) => index;
_renderImageView = (item, index) => {
console.log("LOG: renderItem: " + item);
return(
<Text style={{borderColor: "red", fontSize: 30, justifyContent: 'center', borderWidth: 5, flex: 1}}>{item}</Text>
);
}
//testData: ["item1", "item2", "item3", "item4", "item5", "item6", "item7", "item8", "item9"]
I'm fairly confident this isn't some flex issue, but in case I missed something here's the relevant stylesheets too:
const styles = StyleSheet.create({
container: {
flex: 1
},
overlaycontainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-between'
},
});
So, what I'm expecting to happen is to see a list of text items.
What's happening instead is I see a white screen with nothing on it.
Why is this list not rendering?
You can try replicating this if you want, define FlatList inside your return().
<View>
<FlatList
data={Items}
renderItem={this.renderItems}
enableEmptySections
keyExtractor={item => item.id}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
Then declare your Items inside constant(array of objects) outside your class like this,
const Items = [
{
id: FIRST,
title: 'item1',
},
{
id: SECOND,
title: 'item2',
},
{
id: THIRD,
title: 'item3',
},
// and so on......
];
After this get your items outside render by calling a function
onSelection = (item) => {
console.log(item.title)
}
renderItems = ({ item }) => {
return(
<TouchableOpacity
onPress={() => this.onSelection(item)}>
<View>
<Text>
{item.title}
</Text>
</View>
</TouchableOpacity>
)
}
Are you not missing the return on your renderItem?
render() {
console.log("LOG: GalleryView render function called");
console.log("FlatList data: " + this.state.testData.toString());
return(
<FlatList
data={this.state.testData}
keyExtractor={this._keyExtractor}
style={styles.container}
renderItem={
({item, index}) => {
return this._renderImageView(item, index);
}
}
/>
);
}
}

Access section data from section items in react native SectionList

I need to access the information regarding the section (index, value) inside the renderItem in react-native SectionList. According to http://docs.w3cub.com/react_native/sectionlist/#renderitem section details can be passed via renderItem function. But in below code except the item all other values will be set to undefined. Is there any other possible way of doing it?
render(){
return(
<SectionList
sections={this.props.itemList}
renderItem={(item,section) => this._renderNewItem(item,section)}
renderSectionHeader={this._renderSectionHeader}
keyExtractor={(item) => item.id}
/>
)
}
_renderNewItem(item,section){
console.log(item, " ", section)
}
Sample data structure
renderItem prop passes a single parameter to the function. This parameter is an object includes item and section data.
renderItem: (info: { item: Item, index: number, section: SectionT,
separators: { highlight: () => void, unhighlight: () => void,
updateProps: (select: 'leading' | 'trailing', newProps: Object) =>
void, }, }) => ?React.Element
To get the section data you can use it like below
renderItem={({ item, section }) => this._renderNewItem(item,section)}
Update
Adding a sample example to demonstrate how it works. See it on snack.expo.io
import React, { Component } from 'react';
import { Text, View, StyleSheet, SectionList } from 'react-native';
import { Constants } from 'expo';
const data = [{key: 'New', data: [{name: 'Foo1'}, {name: 'Foo2'}]}, {key: 'Old', data: [{name:'Foo3'}, {name: 'Foo4'}]}];
export default class App extends Component {
_renderItem = ({item, section}) => (<Text>{`${item.name}(${section.key})`}</Text>)
_renderSectionHeader = ({section}) => {
return (
<View style={styles.sectionHeader}>
<Text style={styles.header}>{section.key}</Text>
</View>
)
}
render() {
return (
<View style={styles.container}>
<SectionList
sections={data}
renderItem={this._renderItem}
renderSectionHeader={this._renderSectionHeader}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Constants.statusBarHeight,
backgroundColor: '#ecf0f1',
},
sectionHeader: {
height: 50,
flex: 1,
backgroundColor: '#fff',
justifyContent: 'center',
paddingLeft: 10
},
header: {
fontSize: 20,
}
});
Output of this will look like attached image Link
https://i.stack.imgur.com/Y7CLF.png Output of this solution image.
https://pastebin.com/embed_js/7kYrk8kf Dummy JSON File URL Link for this Demo.
I used above mentioned JSON with Section List in following way with the help of #bennygenel code.
renderItem = ({ item, section }) => <Text>{`${item.title}`}</Text>;
renderSectionHeader = ({ section }) => {
return (
/* <View style={styles.sectionHeader}>
<Text style={styles.header}>{section.key}</Text>
</View> */
****Custom Header Component ****
<CellHeader title={section.title} />
);
};
renderSectionList = () => {
return (
<View>
<SectionList
sections={data1.results}
renderItem={this.renderItem}
renderSectionHeader={this.renderSectionHeader}
/>
</View>
);
};