ReactNative FlatList onEndReached calling even when not scrolling - react-native

Im trying to implement Flatlist of Somedata which contains almost 200 elements in an array that im passing in data.
Im trying to give user the option to load the rest when they scroll only. but what happening with onEndReached is, it is calling even though we are not scrolling (I checked by doing console log). How can I make sure onEndReached is calling only when user scrolls.
I tried setting onEndReachedThreshold to the max of 5 and min of 0.01 in both the cases it is not working. Tried this too but didn't work https://github.com/facebook/react-native/issues/14015#issuecomment-310675650.
<FlatList
data={this.state.properties}
showsVerticalScrollIndicator={false}
keyExtractor={item => item.mlsnum}
renderItem={({ item }) => <Text{item.title}</Text>}
onEndReachedThreshold={0.01}
onEndReached={() => this.handleEndReach()}
/>
async handleEndReach() {
this.props.fetchProperties(pageNum) //call to my redux action to fetch the data
}

it's a bad implementation using async in there, you should use it in the function not in the callback. Please just use
onEndReached={() => this.fetchProperties())
and then for the function use
async fetchProperties(){
//do your async/await here
}

Related

Swipe up or pull up to refresh in React-Native

I am trying to implement a feature in which whenever I went to end the end of the FlatList and then when I swipe up or pull up for some small time, then this acts as a refresh and required data get loaded. Also the flatlist is long so we reach the end of the list in some time. Please help in this as I can't get any resources available for the same.
I tried using various packages like react-native-gesture-handler etc. but couldn't get the solution which I am hoping for.
Reaching the end of FlatlList amd pulling up are two different thing
For Reaching end of the list You can detect by onEndReached callback.
For Refreshing (Pull to refresh) You can use [RefreshControl][1]
Please see the below Example
// Logic for onEndReached
const onEndReached= ()=>{
do Your styff
}
<FlatList
refreshControl={
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}>
data={yourArray}
renderItem={yourRenderItem}
onEndReached= {onEndReached}
/>
you can use onMomentumScrollEnd which is provided by FlatList
const handleMomentumScrollEnd = () => {
clearTimeout(scrollTimeout.current);
if (prevY.current > 50) {
setRefreshing(true);
setTimeout(() => {
setRefreshing(false);
}, 2000);
}
};
you can check this expo snack link to view the full code.
Expo

Refresh a Flatlist if data from function inside componentDidMount is changed

Good day SO.
I am a beginner in React Native so my terminology is very lacking. The project was inherited from previous programmer.
I have an async functions
getData = async () => {}
Then here:
componentDidMount() {
this.getData ();
}
Then last is Flatlist
<FlatList
data={this.state.somedata}
renderItem{({item, index}) => {}} />
My question is, if data is changed, how should I update my flatlist? I read that I need to add extraData but I dont know what variable should I add inside.
Note I know that componentDidMount only runs once on the first render. But I dont know how to proceed.

Render FlatList of Videos in a performant way

I am using a react native with expo. I have a lot of videos that I need to render (sort of like TikTok does). When I fetch about 30 videos and put them in the flat list in the renderItem method, it gets stuck and luggish. I was thinking about getting an amount of videos but sending to the renderItem method only 3 videos each time, and when the user will scroll down and reach index 2 it will shift the first index and append the fourth video from the fetched one. The idea was to have a small array of size 3 and change the items in it every scroll, in order to prevent rendering all the videos at once. That required array manipulation and caused a rerender each time the array of videos was updated(each change made sort of a flash - what was indicating a whole rerender).
My question is how should it be implemented in order the transition between the videos to be as fast and clean as possible from the client side perspective? What is the correct way to render videos in a flat list so it won't be stuck? I dont think It should be done that way, there has to be a better way.
This is what I have tried:
// challenges is an array coming from a fetch, just sliced it for the purpose of the example
// suppose it is an array that contains 30 items
const [currentVideos, setCurrentVideos] = useState([challenges.slice(0,3)]);
<FlatList
data={currentVideos}
renderItem={renderItem}
keyExtractor={(challenge, i) => challenge._id}
showsVerticalScrollIndicator={false}
snapToInterval={Dimensions.get("window").height - UIConsts.bottomNavbarHeight}
snapToAlignment={"start"}
decelerationRate={"fast"}
ref={(ref) => {
flatListRef.current = ref;
}}
onScrollToIndexFailed={() => alert("no such index")}
onViewableItemsChanged={onViewRef.current}
onScrollEndDrag={() => (scrollEnded.current = true)}
onScrollBeginDrag={beginDarg}
></FlatList>
useEffect(() => {
// just wanted to check on 3 videos
if (currentlyPlaying === 2) {
let temp = currentVideos;
temp.shift(); // pop the top item
temp.push(challenges[4]) // append a new one
setCurrentVideos(temp);
}
}, [currentlyPlaying]);
const onViewRef = useRef(({ viewableItems }) => {
// change playing video only after user stop dragging
scrollEnded.current && setCurrentlyPlaying(viewableItems[0]?.index);
});
I would avoid manipulating the data array and doing business logic inside of the component.
Besides, you can achieve your desired behaviour without the need to manipulate your data array at all, with the maxToRenderPerBatch FlatList prop. As mentioned in the official RN docs for FlatList optimization techniques.
You should avoid using anonymous functions and objects inside of your component's properties, move them outside of the return statement and use the useMemo and useCallback hooks to avoid their unnecessary recreation on every re-render. For example instead of writing your code like this:
const App = () => {
return (
<FlatList
keyExtractor={(challenge, i) => challenge._id}
snapToInterval={Dimensions.get('window').height - UIConsts.bottomNavbarHeight}
/>
);
};
A better approach would be to re-write it to something like this:
const App = () => {
// Because of useCallback, the keyExtractor function will be memoized and won't recreate itself on every re-render
const keyExtractor = useCallback((challenge, i) => challenge._id, []);
// useMemo is almost the same as useCallback, but it is used to return non-function types
// Defining your snapToInterval variable like this will cause it to memoize its value and it
// won't recreate itself on every re-render
const snapToInterval = useMemo(() => Dimensions.get('window').height - UIConsts.bottomNavbarHeight, []);
return (
<FlatList
keyExtractor={keyExtractor}
snapToInterval={snapToInterval}
/>
);
};
If you haven't already, you should consider extracting the component returned from the renderItem function to a different file and applying React.memo to it.
Note: try not to overuse useCallback and useMemo. You can find good and detailed explanation of why not to overuse them here and here.
If you're able to, you should optimize your videos before uploading them to the server. You can optimize your client side part of the app as much as you want, but if the content isn't properly optimized, you won't be able to achieve a smooth and performant experience regardless of your efforts.
Here's also some articles describing how you can optimize your FlatList component:
How did I optimize my React Native FlatList?
8 ways to optimize React native FlatList performance
Optimizing a React Native FlatList With Many Child Components
React Native Performance Optimisation With Hooks
React Native: Optimized FlatList of videos
I hope that some of this will be helpful to you. Good luck.
I have been searching for a solution as well. I have worked out a solution based on some previous work using InViewPort. you can check it out here https://github.com/471Q/React-Native-FlatList-Video-Feed

React-Native: How to know when the rendering got finished

I have FlatList in render() function with a reference,
<FlatList
ref={(ref) => { this.flatListRef = ref }}
data={this.state.data}
renderItem={({ item, index }) => this.renderOuterCard(item, index)}
/>
I need to use this.flatListRef to call FlatList's methods, which is possible only when FlatList is rendered. Otherwise following error occurs,
Cannot read the property 'ScrollToIndex' of undefined
(I tried to call FlatList's ScrollTOIndex method)
Infact I called ScrollTOIndex in componentDidMount() method , But still this error occurs. Hence, This is clear that componentDidMount() is called before render() finishes completely.
Can anybody let me know when rendering finishes exactly?
This is clear that componentDidMount() is called before render() finishes completely.
According to this schema the first render phase occur before componentDidMount.
http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
 Live example:
https://snack.expo.io/#flexbox/flatlist-with-state-example
I am having the same issue. I do have a Component that uses FlatList and I would like to know when, within the first render, the list has rendered the visible items (ready state).
I have discovered that on the main component the componentDidMount was triggered because the FlatList component have mounted as well. And because FlatList Component asyncronously renders each item they only show up after on componentDidUpdate.
I have struggled for a while and even with the componentDidUpdate approach I could only get the moment where FlatList got new data (not rendered the items).
So I found out that I could intercept the renderItem method to create some logic to better estimate what and when is being rendered. The other option was to have a setTimeout (very hacky) to trigger the work based on a average time (very poor solution).
If someone have a proper approach to be able to know when the FlatList finishes rendering the inView items please share.
Thanks.

Using FlatList#onViewableItemsChanged to call a Component function

I'm currently attempting to implement a form of LazyLoading using the FlatList component, which introduces a neat little feature called onViewableItemsChanged which gives you a list of all of the components that are no longer on the screen as well as items that are now on the screen.
This is a custom LazyLoad implementation and as such is more complicated than most LazyLoad open-sourced libraries that are available, which is why I'm working on my own implementation. I'm already looked into react-native-lazy-load and others.
Basically, I need to be able to call a function that's part of the component being rendered in the FlatList, I've tried creating a reference to the item rendered in the FlatList and calling it as such, but it doesn't seem to work.
For example:
<FlatList data={...}
renderItem={(item) => <Example ref={(ref) => this[`swiperRef_${item.key}`] = ref}}
onViewableItemsChanged={this.onViewableItemsChanged}
/>
onViewableItemsChanged = ({viewableItems}) => {
viewableItems.forEach((item) => {
const { isViewable, key } = item;
if(isViewable && !this.cachedKeys.includes(key)) {
const ref = this[`swiperRef_${key}`];
if(!ref) return console.error('Ref not found');
ref.startLoading();
this.cachedKeys.push(key);
}
});
}
Now in the <Example /> component I would have a function called startLoading which should be called when a new visible item is brought onto the screen, however the ref never exists.
I was actually doing everything correctly, but I accidently forgot to deconstruct the parameter returned from the renderItem function, so (item) should have been ({ item })
That's all there was to it.