UNPKG

3.15 kBJavaScriptView Raw
1import * as React from 'react';
2import { useNavigation, useRoute } from '@react-navigation/core';
3
4function getScrollableNode(ref) {
5 if (ref.current == null) {
6 return null;
7 }
8
9 if ('scrollToTop' in ref.current || 'scrollTo' in ref.current || 'scrollToOffset' in ref.current || 'scrollResponderScrollTo' in ref.current) {
10 // This is already a scrollable node.
11 return ref.current;
12 } else if ('getScrollResponder' in ref.current) {
13 // If the view is a wrapper like FlatList, SectionList etc.
14 // We need to use `getScrollResponder` to get access to the scroll responder
15 return ref.current.getScrollResponder();
16 } else if ('getNode' in ref.current) {
17 // When a `ScrollView` is wraped in `Animated.createAnimatedComponent`
18 // we need to use `getNode` to get the ref to the actual scrollview.
19 // Note that `getNode` is deprecated in newer versions of react-native
20 // this is why we check if we already have a scrollable node above.
21 return ref.current.getNode();
22 } else {
23 return ref.current;
24 }
25}
26
27export default function useScrollToTop(ref) {
28 const navigation = useNavigation();
29 const route = useRoute();
30 React.useEffect(() => {
31 let current = navigation; // The screen might be inside another navigator such as stack nested in tabs
32 // We need to find the closest tab navigator and add the listener there
33
34 while (current && current.dangerouslyGetState().type !== 'tab') {
35 current = current.dangerouslyGetParent();
36 }
37
38 if (!current) {
39 return;
40 }
41
42 const unsubscribe = current.addListener( // We don't wanna import tab types here to avoid extra deps
43 // in addition, there are multiple tab implementations
44 // @ts-expect-error
45 'tabPress', e => {
46 // We should scroll to top only when the screen is focused
47 const isFocused = navigation.isFocused(); // In a nested stack navigator, tab press resets the stack to first screen
48 // So we should scroll to top only when we are on first screen
49
50 const isFirst = navigation === current || navigation.dangerouslyGetState().routes[0].key === route.key; // Run the operation in the next frame so we're sure all listeners have been run
51 // This is necessary to know if preventDefault() has been called
52
53 requestAnimationFrame(() => {
54 const scrollable = getScrollableNode(ref);
55
56 if (isFocused && isFirst && scrollable && !e.defaultPrevented) {
57 if ('scrollToTop' in scrollable) {
58 scrollable.scrollToTop();
59 } else if ('scrollTo' in scrollable) {
60 scrollable.scrollTo({
61 y: 0,
62 animated: true
63 });
64 } else if ('scrollToOffset' in scrollable) {
65 scrollable.scrollToOffset({
66 offset: 0,
67 animated: true
68 });
69 } else if ('scrollResponderScrollTo' in scrollable) {
70 scrollable.scrollResponderScrollTo({
71 y: 0,
72 animated: true
73 });
74 }
75 }
76 });
77 });
78 return unsubscribe;
79 }, [navigation, ref, route.key]);
80}
81//# sourceMappingURL=useScrollToTop.js.map
\No newline at end of file