react-native
react-native copied to clipboard
(Android/FlatList) Fix onMomentumScrollEnd being called multiple times
Description

Version
0.64.1
Output of react-native info

Steps to reproduce

Snack, code example, screenshot, or link to a repository
No response
Observing the same symptom.
FYI, the onMomentumScrollBegin was triggered only once, but the onMomentumScrollEnd was trigged around 3~5 times.

Same bug occurs on the ScrollView component
+1
Was able to reproduce it on the 0.66.4 version. Android only. Hermes is enabled (maybe it would be useful). I have faced with that issue on 0.64.2 first time. But it's still reproducible.
+1
This is how it worked for me
const [canmomentum, setCanMomentum] = useState(false);
<ScrollView
onScroll={( event ) => {
setCanMomentum(true)
}}
onMomentumScrollEnd={() => {
if (canmomentum) console.log('onMomentumScrollEnd')
setCanMomentum(false)
}}
>
<Content/>
</ScrollView>
This is how it worked for me
const [canmomentum, setCanMomentum] = useState(false); <ScrollView onScroll={( event ) => { setCanMomentum(true) }} onMomentumScrollEnd={() => { if (canmomentum) console.log('onMomentumScrollEnd') setCanMomentum(false) }} > <Content/> </ScrollView>
Thanks for this suggestion, I did something similar but with a ref instead to avoid unnecessary state updates and renders.
This is how it worked for me
const [canmomentum, setCanMomentum] = useState(false); <ScrollView onScroll={( event ) => { setCanMomentum(true) }} onMomentumScrollEnd={() => { if (canmomentum) console.log('onMomentumScrollEnd') setCanMomentum(false) }} > <Content/> </ScrollView>Thanks for this suggestion, I did something similar but with a ref instead to avoid unnecessary state updates and renders.
I didn't find how to reference the scrolling state from ref. would you agree to share your solution?
This is how it worked for me
const [canmomentum, setCanMomentum] = useState(false); <ScrollView onScroll={( event ) => { setCanMomentum(true) }} onMomentumScrollEnd={() => { if (canmomentum) console.log('onMomentumScrollEnd') setCanMomentum(false) }} > <Content/> </ScrollView>Thanks for this suggestion, I did something similar but with a ref instead to avoid unnecessary state updates and renders.
I didn't find how to reference the scrolling state from ref. would you agree to share your solution?
Second this, please share thy precious knowledge of the hooks. Saveth me from mine own perils! @ewnavilae I beg of you.
Solution using useRef:
const canMomentum = React.useRef(false);
const onMomentumScrollBegin = () => {
canMomentum.current = true;
};
const onMomentumScrollEnd = () => {
if (canMomentum.current) {
// console.log('onMomentumScrollEnd');
}
canMomentum.current = false;
};
return (
<ScrollView
onMomentumScrollBegin={onMomentumScrollBegin}
onMomentumScrollEnd={onMomentumScrollEnd}
></ScrollView>
);
I'm having the same issue in version 0.68.2
Still an issue in version 0.69.6
Still an issue in version 0.70
Any update on this? The workaround doesn't work if using the snapToInterval because its the last event that is correct. I can put in a debounce but it is way too long of a debounce since the onMomentumScrollEnd gets called almost a second apart.
When using snapToInterval, and user drags very little and lets go, it snaps back to the original snap location, but onMomentumScrollEnd gets called twice. once for the temp scrolled location and once for the new location
Still an issue in version 0.71.7
+1
+1
+1
Looks like this will be fixed by https://github.com/facebook/react-native/pull/32433.
Note there's a similar problem for onScrollBeginDrag (which gets called twice). I don't know if that PR fixes it.
I'm still having this issue in react-native 0.72.6.
Solution using useRef:
const canMomentum = React.useRef(false); const onMomentumScrollBegin = () => { canMomentum.current = true; }; const onMomentumScrollEnd = () => { if (canMomentum.current) { // console.log('onMomentumScrollEnd'); } canMomentum.current = false; }; return ( <ScrollView onMomentumScrollBegin={onMomentumScrollBegin} onMomentumScrollEnd={onMomentumScrollEnd} ></ScrollView> );
This works but the event is called too early, it doesn't wait for the animation to end.
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
It must be fixed in RN 0.74.1 https://github.com/facebook/react-native/commit/06668fcbacd750771f1d53cce829dc55e86f3f3c
Not fixed :(
Using RN 0.74.3 and can confirm that this issue has been fixed.
This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.
looks like this is fixed, should probably be closed.
tested on expo 52 https://snack.expo.dev/@hichem_fantar/onmomentumscrollend-example
actually it seems the behavior isn't consistent between android and ios. on android it works when you flick and release, it also works when you scroll and hold thereby controlling the scrolling.
on IOS it only works when you flick and let the scroll momentum end by itself.
Android:
https://github.com/user-attachments/assets/706f34b0-c19c-49c2-887c-d035d2119fee
iOS:
https://github.com/user-attachments/assets/9a0cafe0-7370-4359-bd63-4dcc16cb9a51
I created a separate issue here: https://github.com/facebook/react-native/issues/48423 i believe this can be closed now since it's a different issue