react-native-reanimated
react-native-reanimated copied to clipboard
[Android] onMomentumBegin/ -End not being called in Animated.ScrollView
Description
onMomentumEnd is not being called anymore since Version 2.3.0 in Animated.ScrollView
. An animated FlatList
or SecionList
(with createAnimatedComponent
) still calls onMomentumEnd
. Am I doing something wrong in my code? In Version 2.2.4 it still worked.
This causes a bug in this library as well: https://github.com/PedroBern/react-native-collapsible-tab-view/issues/221
Expected behavior
I expect onMomentumEnd to be called like in version 2.2.4 and lower.
Actual behavior & steps to reproduce
Scroll the screen of the code I provided. "onMomentumEnd" will not be printed in the console.
Snack or minimal code example
import React from 'react';
import { View } from 'react-native';
import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated';
export default function App() {
const translationY = useSharedValue(0);
const isScrolling = useSharedValue(false);
const scrollHandler = useAnimatedScrollHandler({
onScroll: (event) => {
translationY.value = event.contentOffset.y;
},
onBeginDrag: (e) => {
isScrolling.value = true;
console.log('onBeginDrag');
},
onEndDrag: (e) => {
isScrolling.value = false;
console.log('onEndDrag');
},
onMomentumEnd: () => {
console.log('onMomentumEnd');
},
});
return (
<View style={{flex:1}} >
<Animated.ScrollView
onScroll={scrollHandler}
scrollEventThrottle={16}>
<View style={{height:250, width:300, backgroundColor:"red"}}></View>
<View style={{height:250, width:300, backgroundColor:"blue"}}></View>
<View style={{height:250, width:300, backgroundColor:"yellow"}}></View>
<View style={{height:250, width:300, backgroundColor:"purple"}}></View>
<View style={{height:250, width:300, backgroundColor:"green"}}></View>
</Animated.ScrollView>
</View>
);
}
Package versions
- React Native: 0.64.3
- React Native Reanimated: 2.3.0
- NodeJS: 16.6.2
- Xcode: - Just tested on android
- Java & Gradle: I tested in expo SDK 44 beta
Affected platforms
- [x] Android
- [ ] iOS
- [ ] Web
Issue validator
The issue is valid!
Just as a small update. Tried it with version 2.3.1. The bug is still there
@piaskowyk Did you have a chance to test my snippet? Can I support you somehow? Others seem to have the problem as well: https://github.com/PedroBern/react-native-collapsible-tab-view/issues/221#issuecomment-997474612
Not only onMomentumEnd but also onMomentumBegin is not called either. It's very annoying.
Guys, passing a function to onMomentumScrollEnd prop works for me. The onMomentumScrollEnd is called using the useAnimatedScrollHandler
hook, as expected.
const handleScrollHandler = useAnimatedScrollHandler({
onMomentumScrollEnd: () => {
// was called
}
})
function handleMomentumScrollEnd() {
// workaround reanimated 2.3 momentum scroll is not called
}
<Animated.ScrollView onScroll={handleScrollHandler} onMomentumScrollEnd={handleMomentumScrollEnd} />
@fhugoduarte Are you sure you are running rea 2.3.0? Could you try my code snippet?
@fhugoduarte Are you sure you are running rea 2.3.0? Could you try my code snippet?
From my package.json: "react-native-reanimated": "~2.3.1",
"react-native": "0.64.3",
@alexco2 Even I was facing the same issue and after spending hours of debugging I found that, if we add onMomentumScrollEnd
prop to the FlatList or ScrollView, then onMomentumEnd
handler was triggered correctly
This is what I did:
<Animated.ScrollView
ref={ref}
onMomentumScrollEnd={() => {}}
{...otherProps}
/>
I've got this bug too (reanimated 2.3.1). Unfortunately, the previous workaround didn't help me. In my case, I needed to handle the change of the current item in my horizontal ScrollView. I've solved it with a set of reanimated hooks, so maybe it can be helpful for someone:
const offsetX = useSharedValue(0);
const currentIndex = useDerivedValue(
() => Math.round(offsetX.value / galleryWidth),
[galleryWidth],
);
useAnimatedReaction(
() => currentIndex.value,
(index, prevIndex) => {
if (index !== prevIndex && typeof onChangeItem === 'function') {
runOnJS(onChangeItem)(items[index]);
}
},
[currentIndex, items, onChangeItem],
);
const onScroll = useAnimatedScrollHandler({
onScroll: event => {
offsetX.value = event.contentOffset.x;
},
});
Just as an information. The issue still persists with version 2.5.0. @piaskowyk do you have any updates on the progress of this bug?
I experience a similar issue on iOS with version 2.5.0. onEndDrag / onMomentumEnd are called on the simulator, but they do not get called when the app is deployed to a device.
Hello maintainers, what would it take to get some attention to this? (🙏 @piaskowyk)
It's blocking users of react-native-collapsible-tab-view
from upgrading to RN 0.68 because Reanimated versions above 2.3.0 break the library due to this bug.
I can confirm this issues. Sad this isn't resolved since Dec :( Anything we can do to help?
@piaskowyk it would be amazing if we could have an update on the progress of this issue :)
Just leaving a comment to bring more attention to this 🙏 It's also a blocker for us to upgrade to RN 0.68
Everyone affected by this, don't forget to upvote the top post. Perhaps it helps to get it more visibility.
Hello @kmagiera @piaskowyk @Szymon20000 @jkadamczyk, this issue is now the third most interacted issue (regarding thumbs up) in rea. Could you give us an update on the current state? Is there anything we could do to support you in solving this issue? Thanks :)
@piaskowyk sorry to be the 20th person who pings you here, but this issue really needs some hands on to get resolved. It is actually a very annoying blocker :/
Can we do something to get some traction here? Obviously nobody here has enough native knowledge to provide a PR.
@piaskowyk sorry to be the 20th person who pings you here, but this issue really needs some hands on to get resolved. It is actually a very annoying blocker :/
Can we do something to get some traction here? Obviously nobody here has enough native knowledge to provide a PR.
I really agree. I think it is not to much to ask for at least a small update so the community knows how to handle this issue. Also the amount of reactions clearly show a big interest in this issue. Sorry for pinging all of you, but @piaskowyk does not seem to be responsible for this issue anymore.
@kmagiera @Szymon20000 @jkadamczyk
I just found out something interesting.
Seems that if I add an empty onMomentumScrollEnd
handler to the ScrollView
, it starts firing the event and the repro at the very top starts logging onMomentumEnd

Example:
<Animated.ScrollView onScroll={scrollHandler}
scrollEventThrottle={16}
// add this:
onMomentumScrollEnd={()=>{}}>
Can anyone else confirm this? Note that it also seems to log onMomentumEnd
three times, so something is weird with it.
@alexco2 Even I was facing the same issue and after spending hours of debugging I found that, if we add
onMomentumScrollEnd
prop to the FlatList or ScrollView, thenonMomentumEnd
handler was triggered correctly This is what I did:<Animated.ScrollView ref={ref} onMomentumScrollEnd={() => {}} {...otherProps} />
@andreialecu So basically what @BabuStack found out, right? At least for @danilichev this workaround did not help. I will try this workaround later today and give you feedback. But apparently this is a somewhat hacky workaround.
Indeed, yes, I missed that comment. I didn't really look into this until today so it seems I stumbled upon the same fix.
It does seem to fix the repro, and based on the code in react-native-collapsible-tab-view
that uses onMomentumEnd
it should be good enough even if it's called multiple times.
I'm working on testing it myself in a real app soon.
@andreialecu if you release a patch for react-native-collapsible-tab-view I can test it as well in my current production app. I hope this issue gets resolved anyway soon so we don't need any workarounds :)
I don't think there's a need to patch it, you can just try <Tabs.ScrollView ... onMomentumScrollEnd={() => {}} />
Edit: I can confirm I'm now testing on Reanimated 2.8.0 and the above workaround seems to help.
@andreialecu Can you still navigate by pressing the tabs when the header is not fully collapsed or extended (e.g. extended 50%)
@andreialecu even with the workaround, there are still issues. Scrollview for example does not snap anymore and navigating between tabs by clicking the header does not work as well.
Try react-native-collapsible-tab-view@rc
https://github.com/PedroBern/react-native-collapsible-tab-view/pull/255
Let's move this discussion over there as to not hijack this thread. 🙂
This is actually not reanimated
related problem, it's that RN on Android doesn't sent momentum events by default:
https://github.com/facebook/react-native/blob/0e06185f43763f2e8daecbd4dd5dd53917a795ff/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollViewManager.java#L138-L149
One can turn it on either with https://github.com/facebook/react-native/blob/ef6ab3f5cad968d7b2c9127d834429b0f4e1b2cf/Libraries/Components/ScrollView/ScrollView.js#L1741-L1744 or https://github.com/facebook/react-native/blob/ef6ab3f5cad968d7b2c9127d834429b0f4e1b2cf/Libraries/Components/ScrollView/ScrollView.js#L385-L391
Just ran into this, the workaround works but would be good to understand if there's anything we can do to help solve this.
Thanks @alexco2 for reporting this issue. Also, thank you @fhugoduarte for providing a workaround in https://github.com/software-mansion/react-native-reanimated/issues/2735#issuecomment-1001714779 as well as @savelichalex for the explanation why it works this way in https://github.com/software-mansion/react-native-reanimated/issues/2735#issuecomment-1177923431.
The problem occurs only on Android (both on Paper and Fabric), onMomentumEnd
is properly emitted on iOS. I've submitted a PR that should fix it, see #3948.
Apart from this, using the example @alexco2 provided in the issue description, I can confirm @andreialecu's observation from https://github.com/software-mansion/react-native-reanimated/issues/2735#issuecomment-1117067988 that onMomentumEnd
callback is always called exactly 3 times with the same event values (personally, I would expect it to be called exactly once):
onBeginDrag
onEndDrag
onMomentumEnd {"contentInset": {"bottom": 0, "left": 0, "right": 0, "top": 0}, "contentOffset": {"x": 0, "y": 4026.54541015625}, "contentSize": {"height": 5000, "width": 392.7272644042969}, "eventName": "55onMomentumScrollEnd", "layoutMeasurement": {"height": 778.9091186523438, "width": 392.7272644042969}, "responderIgnoreScroll": true, "target": 55, "velocity": {"x": 0, "y": 0}}
onMomentumEnd {"contentInset": {"bottom": 0, "left": 0, "right": 0, "top": 0}, "contentOffset": {"x": 0, "y": 4026.54541015625}, "contentSize": {"height": 5000, "width": 392.7272644042969}, "eventName": "55onMomentumScrollEnd", "layoutMeasurement": {"height": 778.9091186523438, "width": 392.7272644042969}, "responderIgnoreScroll": true, "target": 55, "velocity": {"x": 0, "y": 0}}
onMomentumEnd {"contentInset": {"bottom": 0, "left": 0, "right": 0, "top": 0}, "contentOffset": {"x": 0, "y": 4026.54541015625}, "contentSize": {"height": 5000, "width": 392.7272644042969}, "eventName": "55onMomentumScrollEnd", "layoutMeasurement": {"height": 778.9091186523438, "width": 392.7272644042969}, "responderIgnoreScroll": true, "target": 55, "velocity": {"x": 0, "y": 0}}
On the other hand, ReactScrollViewHelper#emitScrollMomentumEndEvent
is also called 3 times (checked by setting a breakpoint inside the method), so I guess it's the correct behavior from Reanimated side (to intercept all three events that were emitted) and probably there's an issue somewhere deeper (RN/Android).
Also, the original name of the event is onMomentumScrollEnd
, but useAnimatedScrollHandler
accepts the worklet callback in onMomentumEnd
field (without Scroll
). Do you think we should add support for the original name (so effectively both onMomentumEnd
and onMomentumScrollEnd
work), deprecate the old name in v2 and completely remove it in v3 or just leave it as it is now?