React Native FlatList with inverted option enabled - react-native

I have a FlatList as shown below:
<FlatList
inverted
data={messages}
keyExtractor={this._keyExtractor}
renderItem={({ item }) => (
<Text style={styles.item}>{item}</Text>
)}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={30}
/>
But here the OnEndReached method does not gets called when I reach the top of the flatlist.
Please help

OnEndReachedThreshold must be a number between 0 and 1. Since you are inverting your flatlist, onEndReachedThreshold would be the distance the user is from the top of the list [in percents]. Therefore a value of 0.5 would trigger the OnEndReached function when the user has scrolled through 50% of the viewable list.
To trigger the function at 50% your code should read something like this:
<FlatList
inverted
data={messages}
keyExtractor={this._keyExtractor}
renderItem={({ item }) => (
<Text style={styles.item}>{item}</Text>
)}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={0.5}
/>

All I could figure out was using of onScroll (performance beware) in here: https://snack.expo.io/#zvona/inverted-list-onbeginreached
The actual function looks like this:
checkIfBeginningReached = ({ nativeEvent }) => {
const { layoutMeasurement, contentOffset } = nativeEvent;
const currentPos = layoutMeasurement.height + contentOffset.y;
const listLength = ITEM_HEIGHT * this.state.items.length;
const reactThreshold = listLength - (ITEM_HEIGHT * THRESHOLD);
if (currentPos >= reactThreshold) {
this.fetchMoreItems(this.state.items.length);
}
}
On that, we pick up necessary info from nativeEvent (which kind of holds everything relevant). Then we just calculate the current position in pixels, length of whole list content in pixels and then threshold point.
In all, this particular solution requires two things:
1) list has fixed and same size of elements
2) list is not multi-column.
All the other functionality in the demo is just faking / mimicking one use case (of fetching 50 more items from server with 500ms delay). But I'll improve my answer if possible. But this should get you started.

My solution is here:
isCloseToBottom = ({layoutMeasurement, contentOffset, contentSize}) => {
const paddingToBottom = 1
return layoutMeasurement.height + contentOffset.y >=
contentSize.height - paddingToBottom}

just set onEndReachedThreshold={0.1} or onEndReachedThreshold={0.2}

Related

PageView's onPageScroll gives wrong position on iPhone 12+

Using "react-native-pager-view": "^6.1.2" package in ReactNative.
I have the same bug as here.
https://github.com/callstack/react-native-pager-view/issues/503
I'm trying PagerView with the code pasted below, but when I swipe to move the page, e.nativeEvent.position is different from the actual page index.
import PagerView, { PagerViewOnPageScrollEvent, PagerViewOnPageSelectedEvent } from 'react-native-pager-view';
import Modal from 'react-native-modal';
const onPageScroll = useCallback((e: PagerViewOnPageScrollEvent) => onPageScrollPagerView(e, setCurrentTabIndex), []);
const onPageScrollPagerView = (
e: PagerViewOnPageScrollEvent,
setCurrentTabIndex: React.Dispatch<React.SetStateAction<number>>,
) => {
console.log(`onPageScroll position = ${e.nativeEvent.position}`);
setCurrentTabIndex(e.nativeEvent.position);
};
return (
<Modal isVisible={isVisible}>
<SafeAreaView>
<View>
<PagerView ref={viewPager} initialPage={0} onPageScroll={onPageScroll} onPageSelected={onPageSelected}>
      {list.map((item) => (
     <View key={item.id}>
    {item.content}
     </View>
      ))}
        </PagerView>
</View>
</SafeAreaView>
</Modal>
);
This is the result obtained when scrolling to the first page.
onPageScroll is called twice, and for some reason the wrong position is returned the second time.
And 0 is set to setCurrentTabIndex.
onPageScroll position = 1
onPageScroll position = 0
Is there any way to resolve this?
It may be related to what you write in <Modal></Modal> of 'react-native-modal'.
As also written here, this problem does not occur on iPhone11, but on iPhone12 and newer devices.
I didn't use 'react-native-modal', I used fullscreen modal.
screenOptions={{ presentation: 'modal' }}

Create a horizontal list to select age

I'm trying to build something like this
I'm doing it in React Native, what you would recommend me to use to create a list like this that I could increase the on focus number?
this is a simple version of what you want.
const App = () => {
const ages = new Array(100).fill(0);
return <FlatList
data={ages}
horizontal
renderItem={({index}) => <View style={{marginHorizontal: 10}}><Text>{index + 1}</Text></View>}
/>
}

Marker not moving on map even after rerendering

I am fetching coordinates from my database every 5 seconds, and my marker is dynamic, this is my render method
console.log('render'+this.state.driverLocation.latitude +' '+this.state.driverLocation.longitude);
let marker = null;
marker = <MapView.Marker title='This is you' coordinate={this.state.driverLocation } />;
return (
<View style={styles.container}>
<MapView
initialRegion={this.state.focusedLocation}
region={!this.state.locationChosen ? this.state.focusedLocation : null}
style={styles.map}
onPress={this.pickLocationHandler}
ref={ref => this.map = ref}
>
{marker}
</MapView>
The log statement in render shows driverLocation changes every 5 sec as it should but the marker stays at the initial position (coordinates given while defining state).
Is there any problem with my code? Do I need to add something? Any help would be appreciated.
The problem was that android relies only on the key to change in order to update the custom marker and it can be solved by assigning a random key to the marker every time there's a need for rerendering.
So, something like this:
marker = <MapView.Marker
title='This is you'
coordinate={this.state.driverLocation}
key={ this.GenerateRandomNumber() }
} />;
While:
GenerateRandomNumber=()=>
{
var RandomNumber = Math.floor(Math.random() * 100) + 1 ;
return RandomNumber;
}

React-native - How to create a scrollable flatList in which the chosen item is the one at the middle?

I would like to create a scrollable FlatList to select only one item among a list. After the user scroll the list, the selected item will be the one in the colored rectangle (which have a fixed position) as you can see here :
Actually I'm only able to render a basic FlatList even after some researches.
Do you know how I should do that ?
I found the solution (but it's not a FlatList) !
To do that I use :
https://github.com/veizz/react-native-picker-scrollview.
To define the background of the current selected items I added a new props highLightBackgroundColor in the ScrollPicker Class in the index file of react-native-picker-scrollview :
render(){
...
let highLightBackgroundColor = this.props.highLightBackgroundColor || '#FFFFFF';
...
let highlightStyle = {
...
backgroundColor: highLightBackgroundColor,
};
...
How to use it :
<ScrollPicker
ref={sp => {
this.sp = sp;
}}
dataSource={['a', 'b', 'c', 'd', 'e']}
selectedIndex={0}
itemHeight={50}
wrapperHeight={250}
highLightBackgroundColor={'lightgreen'}
renderItem={(data, index, isSelected) => {
return (
<View>
<Text>{data}</Text>
</View>
);
}}
onValueChange={(data, selectedIndex) => {
//
}}
/>
How it looks without others customizations:
You can implement the same setup with the very popular react-native-snap-carousel package, using the vertical prop. No need to use a smaller, poorly documented/unmaintained package for this.

React Native. Filter FlatList - error "index=10 count 0"

My task is to filter some array and set it to FlatList.
My filter function is:
updateInvoiceList = (text) => {
let invoiceList = [...this.state.baseInvoiceList];
invoiceList = invoiceList.filter(el => {
return el.name.toLowerCase().includes(text.toLowerCase())
});
this.setState({invoiceList})
}
After filtering, I provide state.invoiceList to FlatList and everything works correctly. But, when I set some symbol which does not exist in my array, for example "!", the function clears the array and it still behaves correctly. When I remove the symbol "!", I get an error screen with:
index=10 count=0
addInArray
ViewGroup.java:5235
addViewInner
ViewGroup.java:5128
addView
ViewGroup.java:4935
addView
ReactViewGroup.java:452
addView
ViewGroup.java:4875
addView
ReactViewManager.java:269
addView
ReactViewManager.java:36
manageChildren
NativeViewHierarchyManager.java:346
execute
UIViewOperationQueue.java:227
run
UIViewOperationQueue.java:917
flushPendingBatches
UIViewOperationQueue.java:1025
access$2600
UIViewOperationQueue.java:46
doFrameGuarded
UIViewOperationQueue.java:1085
doFrame
GuardedFrameCallback.java:29
doFrame
ReactChoreographer.java:166
doFrame
ChoreographerCompat.java:84
run
Choreographer.java:964
doCallbacks
Choreographer.java:790
doFrame
Choreographer.java:721
run
Choreographer.java:951
handleCallback
Handler.java:883
dispatchMessage
Handler.java:100
loop
Looper.java:214
main
ActivityThread.java:7356
invoke
Method.java
run
RuntimeInit.java:492
main
ZygoteInit.java:930
What did I do wrong?
I had the exact same issue with my code, inside a Flatlist and only showing up on Android. I've managed to solve it as follows:
My FlatList had
stickyHeaderIndices={this.state.items[1]}
but apparently the List was loading that stickyHeader ahead of its initialization. From here, the solution consists simply in handling the case in which that item is not initialized yet.
stickyHeaderIndices={this.state.items.length > 0 ? [1] : [0]}
Hope this helps! The solution is pretty straightforward. Debugging it can be a real pain, tho!
On Android, you must not initialise your FlatList stickyHeaderIndices with empty array.
Instead, when your state is not loaded yet, provide FlatList data with some initial data and provide stickyHeaderIndices with [0]
Example code as below
let [data, stickyIndices] = someAPIToGetYourData()
...
...
...
// if your data is not finished loading yet
if (data.length === 0) {
// create not-empty array
data = getInitialNotEmptyData()
// add not-empty index
headerIndexList = [0]
}
return (
<SafeAreaView>
<FlatList
data={data}
stickyHeaderIndices={headerIndexList /* This must not be empty */ }
renderItem={item => renderYourItem(item)}
/>
</SafeAreaView>
)
Hope this help!
I needed kill the FlatList to fix this error, like this:
<View>
{ this.state.data.length > 0 &&
<FlatList
showsVerticalScrollIndicator={false}
refreshing={false}
onRefresh={() => this.onRefresh()}
data={this.state.data}
extraData={this.state}>}
</View>
Hope this helps!
LayoutAnimation causes a similar issue, not only with Flatlist but any kind of lists, check if you are using it in your app and if you have UIManager.setLayoutAnimationEnabledExperimental(true) on your top component, removing it might fix your issue, but will disable the layoutAnimations.
You might find more information here and here
Please improve your function.
updateInvoiceList = (text) => {
let invoiceList = [];
const { baseInvoiceList } = this.state;
if(text){
invoiceList = baseInvoiceList.filter(item => {
return item.name.toLowerCase().includes(text.toLowerCase());
});
}
this.setState({invoiceList});
}