Skip to content
This repository was archived by the owner on Feb 8, 2020. It is now read-only.

Commit 50dea65

Browse files
committed
fix: support scroll to top in navigators nested in tab
1 parent 3d9db6f commit 50dea65

File tree

1 file changed

+32
-10
lines changed

1 file changed

+32
-10
lines changed

packages/native/src/useScrollToTop.tsx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { useNavigation, EventArg } from '@react-navigation/core';
2+
import { useNavigation, useRoute, EventArg } from '@react-navigation/core';
33

44
type 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

Comments
 (0)