How to make a flat list scroll Infinitely in React Native With a Finite Amount of data in Array - react-native

I have flat list horizontally set
with data of 11 items coming from an array, which is fixed and never changes
what I want is when user reaches at the end of flat list while scrolling, the data should remain the same but the first item should
show up in the last and then so on
here is what I have tried so far
<FlatList
{...this.props}
ref={ref => {
this.infListRef = ref;
}}
data={this.props.data}
onScrollEndDrag={this.handleScroll}
initialScrollIndex={0}
scrollEventThrottle={16}
renderItem={this.props.renderItem}
onScroll={({nativeEvent}) => this.checkScroll(nativeEvent)}
horizontal
showsHorizontalScrollIndicator={false}
keyExtractor={item => item.id}
/>
Any help will be highly appreciated.

Basically, implementing onScroll usage is when you want to be noticed when the actually scroll related to the scroll position (aminations for instance). When you would like to be notified when the user reaches, or about to reach, to the end of the FlatList.
You should implement onEndReached and onEndReachedThreshold to handle a better user experience when the user reaches the threshold.
The new data you're getting from the source (server or no matter wherefrom) should be concatenated to existing this.props.data
See good blog post - https://scotch.io/tutorials/implementing-an-infinite-scroll-list-in-react-native
And this SO answers - React Native Infinite Scroll
My solution refers to pagination because infinite scroll is a private case of pagination, it's the exact same approach.

If you want image or video list
One other approach which is kind a hack and an easy one is using react-native-snap-carousel
<Carousel
ref={ (c) => { this._carousel = c; } }
data={this.state.data}
renderItem={this._renderItem.bind(this)}
onSnapToItem={this.handleSnapToItem.bind(this)}
sliderWidth={360}
itemWidth={256}
layout={'default'}
firstItem={0}
itemHeight={20}
sliderHeight={20}
loop
vertical
loopClonesPerSide={100}
/>
Example:
https://snack.expo.io/#kurtesy/react-native-snap-carousel-example

You can achieve this using onEndReached method of Flatlist.
This is the idea behind answer.
state = {
data: [] //your initial data
}
<Flatlist
{…this.props}
extraData={this.state}
onEndReached = {() => {
this.setState((prevState) =>{
data: […prevState,this.state.data]
)}}
/>

Related

(React-Native) FlatList show special Card when scrolling X amount of times

Hey I have a FlatList with Cards Items. And I want to achieve this scenario.
Scenario:
When Scrolling for example through 5 items in the Flatlist I want to show a feedback card instead of an item Card
How Can this by Implemented ?
In the renderItem method, you can access the index of the current card.
My suggestion would be rendering the feedback card right after every 5th item card.
A rough example will look like this (Not tested yet)
const SampleApp = () => {
const renderItem = ({ item, index }) => {
return (
<>
<ItemCard item={item} />
{(index > 0 && index % 5 === 0) ? <FeedbackCard /> : null}
</>
);
};
return <FlatList data={DATA} renderItem={renderItem} />;
};
You can try following - track onScroll event for FlatList, check if you passed enough offset for 5 Rows, compute target row position and insert feedback card there in FlatList data.

Swipe to next screen (infinite)

I have a calendar section in my app where I want to be able to swipe right or left to go to the next or previous day.
I know that you can use createMaterialTopTabNavigator from React Navigation to do something similar, but I'm not sure if it would work in this specific scenario (where I would have an infinite number of screens and wouldn't be able to predefine them).
I also considered using react-native-gesture-handler's Swipeable component to navigate to the next/previous day screen on a left/right swipe but I'm not sure if this is the easiest/best way to approach this problem.
As you mentioned, you can use Tabs or Swipable. There are some other options like using <ifaram> or <WebView> and using a web URL instead of RN components but it's can be tricky and hard to manipulate.
As a simple solution, you can use carousels instead of using multi screen defining navigation. It's very simple. assume that your screens are an image in an image gallery and you can swipe left/right.
There are many libraries, one of the best ones is: react-native-snap-carousle
Take a look at it's example:
import Carousel from 'react-native-snap-carousel';
export class MyCarousel extends Component {
_renderItem = ({item, index}) => {
return (
<View style={styles.slide}>
<Text style={styles.title}>{ item.title }</Text>
</View>
);
}
render () {
return (
<Carousel
ref={(c) => { this._carousel = c; }}
data={this.state.entries}
renderItem={this._renderItem}
sliderWidth={sliderWidth}
itemWidth={itemWidth}
/>
);
}
}
So you can pass a screen/page as _renderItem to the Carousel

React Native - FlatList not invoking renderItem's lifecycle methods on refresh

I want to make a fetch request inside renderItem's componentDidMount method every time the list is refreshed, but the FlatList calls the lifecycle methods only once.
The list
<FlatList data={this.state.dataSource}
renderItem={({item}) => <ListItem imageHref={item.imageHref} />}
keyExtractor={(item, index) => index.toString()}
refreshing={this.state.refreshing}
onRefresh={/* Fetching data from JSON and updating dataSource[] */} />
Inside ListItem component:
render() {
return <Image source={this.state.imageSource} />
}
componentDidMount() {
fetch(this.props.imageHref)
.then(response => {
if(response.status !== 200)
this.setState({imageSource: require('../assets/default-image.png')
else
this.setState({imageSource: {uri: this.props.imageHref}});
}
});
}
I tried calling fetch inside render method but that didn't work either.
I basically want the imageSource to update every time the list is refreshed. Please help.
Because of that ListItem was not change. You have to fetch new icon in onRefresh and pass it into ListItem then all data will be changed when ListView will refresh... if you want to do it inside ListItem you need some interaction with that specific item, for example, some button and if user press it fetch and change icon
i didn't know when your list refreshed , but you can try this this.forceUpdate() you can learn more here force component to re render

Using a paged FlatList, how to get an event on visible page changed?

I am building a simple horizontal React Native FlatList with paging enabled:
<FlatList
horizontal={true}
pagingEnabled={true}
data={...some data...}
renderItem={({item}) => {...some rendering...}}
onViewableItemsChanged={(info) => {... handling ...}}
/>
I would like to get called back only after a new page in the list has been made visible. The behavior that I am seeking is that, as the user swipes the list left and right, the list will page through the items and I want the callback to fire once with the visible items.
The onViewableItemsChanged props is called on every change in visible items which is not what I am looking for, unless I keep track on which page the items belong to.
I am looking for a onViewablePageChanged type of callback.
Any ideas on how to achieve this?
The solution I used was to track the viewable items against the overall list of items to determine the "page" number of viewable items.
By using pagingEnabled, the list will only show pages.
However onViewableItemsChanged is called back on every changes to the viewable items. By comparing the viewable items againts the list of items, one can figure out on which "page" the FlatList is.
This requires that one knows how many items can be displayed in the view. Either calculated based on layout or set in the View and FlatList rendering.
Here's an example:
onViewableItemsChanged = ({viewableItems}) => {
// Get the first viewable item
const firstViewableItem = viewableItems[0].key;
// Get its index into the items
const index = this.state.items.findIndex(item => item.key === firstViewableItem);
// If the index is a multiple of the number of items displayable on the screen
// by checking for a reminder on the modulo operation
if ((index % NB_ITEMS_SCREEN) === 0) {
// get page
const currentPage = index / NB_ITEMS_SCREEN;
if (currentPage !== this.state.currentPage) {
// Do something and update currentPage in this.state
}
}
}
<FlatList
data={this.state.items}
horizontal={true}
pagingEnabled={true}
renderItem={({ item }) => <Text>{item.key}</Text>}
onViewableItemsChanged={this.onViewableItemsChanged}
/>
See this snack
https://snack.expo.io/#visto9259/flatlist-onviewableitemschanged-example
You can achieve visible items by using below code. Try this.
//Method to invoke when item change
onViewableItemsChanged = ({ viewableItems, changed }) => {
console.log("Visible items are", viewableItems);
console.log("Changed in this iteration", changed);
}
render () => {
return (
...
<FlatList
horizontal={true}
pagingEnabled={true}
data={...some data...}
renderItem={({item}) => {...some rendering...}}
onViewableItemsChanged={this.onViewableItemsChanged }
viewabilityConfig={{
itemVisiblePercentThreshold: 50
}}
/>
....
)}

Logical approach to navigation and flatlist

I click on a button on one screen which loads another screen with a flat list which renders many rows. My conundrum is that I hang on the first screen while the flatlist processes.
My best effort so far is a loader, hidden this.props.children to process the flatlist, then hide the loader and show this.props.children again.
I'm sure there must be a more logical approach?
Ta,
Chris
first instead ho hiding flatlist and showing loader you can use
Refresh Control from React-native. which will we do the same same job
with ease.
second if your list is long you can lazy load your data by using onEndReached Callback this will decrease the loading time .
third make your renderComponent in flatlist a pure Component .which will stop unecessary refreshes and increase performance.
check the code below .....
<FlatList
data={this.props.questions}
renderItem={({ item, index }) => <QuestionCard //your pure component
item={item}
index={index}
onPress={this.openQuestionDetail}
/>
}
ref={'flatlist'}
refreshControl={
<RefreshControl //imported from react-native
refreshing={isFetching} //used to show loader while loading
onRefresh={this.fetchQuestions}
title="Pull to refresh"
tintColor="#fff"
titleColor="#fff"
colors={[Colors.secondary_button]}
/>
}
onEndReached={this.handleDynamicLoading} //callback for lazyloading
onEndReachedThreshold={0.5}
keyExtractor={(item, index) => item.qid}
/>
hope this helps... :)