11import * as React from 'react' ;
2- import { useNavigation , EventArg } from '@react-navigation/core' ;
2+ import { useNavigation , useRoute , EventArg } from '@react-navigation/core' ;
33
44type ScrollOptions = { y ?: number ; animated ?: boolean } ;
55
@@ -36,22 +36,42 @@ export default function useScrollToTop(
3636 ref : React . RefObject < ScrollableWrapper >
3737) {
3838 const navigation = useNavigation ( ) ;
39+ const route = useRoute ( ) ;
3940
40- React . useEffect (
41- ( ) =>
42- // @ts -ignore
41+ React . useEffect ( ( ) => {
42+ let current = navigation ;
43+
44+ // The screen might be inside another navigator such as stack nested in tabs
45+ // We need to find the closest tab navigator and add the listener there
46+ while ( current && current . dangerouslyGetState ( ) . type !== 'tab' ) {
47+ current = current . dangerouslyGetParent ( ) ;
48+ }
49+
50+ if ( ! current ) {
51+ return ;
52+ }
53+
54+ const unsubscribe = current . addListener (
4355 // We don't wanna import tab types here to avoid extra deps
4456 // in addition, there are multiple tab implementations
45- navigation . addListener ( 'tabPress' , ( e : EventArg < 'tabPress' > ) => {
57+ // @ts -ignore
58+ 'tabPress' ,
59+ ( e : EventArg < 'tabPress' > ) => {
60+ // We should scroll to top only when the screen is focused
4661 const isFocused = navigation . isFocused ( ) ;
4762
63+ // In a nested stack navigator, tab press resets the stack to first screen
64+ // So we should scroll to top only when we are on first screen
65+ const isFirst =
66+ navigation === current ||
67+ navigation . dangerouslyGetState ( ) . routes [ 0 ] . key === route . key ;
68+
4869 // Run the operation in the next frame so we're sure all listeners have been run
4970 // This is necessary to know if preventDefault() has been called
5071 requestAnimationFrame ( ( ) => {
5172 const scrollable = getScrollableNode ( ref ) ;
5273
53- if ( isFocused && ! e . defaultPrevented && scrollable ) {
54- // When user taps on already focused tab, scroll to top
74+ if ( isFocused && isFirst && scrollable && ! e . defaultPrevented ) {
5575 if ( 'scrollToTop' in scrollable ) {
5676 scrollable . scrollToTop ( ) ;
5777 } else if ( 'scrollTo' in scrollable ) {
@@ -63,7 +83,9 @@ export default function useScrollToTop(
6383 }
6484 }
6585 } ) ;
66- } ) ,
67- [ navigation , ref ]
68- ) ;
86+ }
87+ ) ;
88+
89+ return unsubscribe ;
90+ } , [ navigation , ref , route . key ] ) ;
6991}
0 commit comments