Skip to content

Commit 7f704bb

Browse files
authored
fix: remove useScrollViewOffset offset in favor of useAnimatedScrollHandler (#3)
1 parent 37edca9 commit 7f704bb

File tree

7 files changed

+178
-110
lines changed

7 files changed

+178
-110
lines changed

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,17 @@ React Native Header is a high-performance, cross-platform animated header compon
1616
- Compatible with [Expo Go](https://docs.expo.dev/) and [React Native CLI](https://github.com/react-native-community/cli) projects
1717
- Written in TypeScript
1818

19+
## Supported dependency versions
20+
21+
| This library | react-native | react-native-reanimated | react-native-safe-area-context |
22+
| -- | -- | -- | -- |
23+
| 0.6.x | >= 0.65 | >= 2.11.0 | >= 4.1.0 |
24+
1925
## Prerequisites
2026

2127
Before you can use `react-native-header`, you need to have the following libraries set up in your React Native project:
2228

23-
- [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated) v2 or v3. **v1 is not supported.**
29+
- [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated)
2430
- [react-native-safe-area-context](https://github.com/th3rdwave/react-native-safe-area-context)
2531

2632
If you haven't installed these libraries yet, please follow the installation instructions on their respective documentation pages.
Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
import React from 'react';
2-
import { View, FlatList, FlatListProps, StyleSheet } from 'react-native';
2+
import { View, FlatListProps, StyleSheet } from 'react-native';
33
import { useSafeAreaInsets } from 'react-native-safe-area-context';
4-
import Animated, {
5-
useAnimatedRef,
6-
useScrollViewOffset,
7-
AnimateProps,
8-
} from 'react-native-reanimated';
4+
import Animated, { useAnimatedRef, AnimateProps } from 'react-native-reanimated';
95

106
import FadingView from '../containers/FadingView';
117
import { useScrollContainerLogic } from './useScrollContainerLogic';
@@ -16,40 +12,45 @@ type AnimatedFlatListType<ItemT = any> = React.ComponentClass<
1612
ItemT
1713
>;
1814
type AnimatedFlatListBaseProps<ItemT> = React.ComponentProps<AnimatedFlatListType<ItemT>>;
19-
type AnimatedFlatListProps<ItemT> = AnimatedFlatListBaseProps<ItemT> & {
20-
children?: React.ReactNode;
21-
};
22-
const AnimatedFlatList: AnimatedFlatListType = Animated.createAnimatedComponent(FlatList);
15+
type AnimatedFlatListProps<ItemT> = AnimatedFlatListBaseProps<ItemT>;
2316

24-
const AnimatedFlatListWithHeaders = <ItemT extends unknown>({
25-
largeHeaderShown,
26-
containerStyle,
27-
LargeHeaderComponent,
28-
largeHeaderContainerStyle,
29-
HeaderComponent,
30-
onLargeHeaderLayout,
31-
onScrollBeginDrag,
32-
onScrollEndDrag,
33-
onMomentumScrollBegin,
34-
onMomentumScrollEnd,
35-
ignoreLeftSafeArea,
36-
ignoreRightSafeArea,
37-
disableAutoFixScroll,
38-
...rest
39-
}: AnimatedFlatListProps<ItemT> & SharedScrollContainerProps) => {
17+
const FlatListWithHeadersInputComp = <ItemT extends unknown>(
18+
{
19+
largeHeaderShown,
20+
containerStyle,
21+
LargeHeaderComponent,
22+
largeHeaderContainerStyle,
23+
HeaderComponent,
24+
onLargeHeaderLayout,
25+
onScrollBeginDrag,
26+
onScrollEndDrag,
27+
onMomentumScrollBegin,
28+
onMomentumScrollEnd,
29+
ignoreLeftSafeArea,
30+
ignoreRightSafeArea,
31+
disableAutoFixScroll,
32+
/** At the moment, we will not allow overriding of this since the scrollHandler needs it. */
33+
onScroll: _unusedOnScroll,
34+
...rest
35+
}: AnimatedFlatListProps<ItemT> & SharedScrollContainerProps,
36+
ref: React.Ref<AnimatedFlatListType<ItemT>>
37+
) => {
4038
const insets = useSafeAreaInsets();
4139
const scrollRef = useAnimatedRef<Animated.FlatList<ItemT>>();
42-
// Need to use `any` here because useScrollViewOffset is not typed for Animated.FlatList
43-
const scrollY = useScrollViewOffset(scrollRef as any);
4440

45-
const { showNavBar, largeHeaderHeight, largeHeaderOpacity, debouncedFixScroll } =
46-
useScrollContainerLogic({
47-
scrollRef,
48-
scrollY,
49-
largeHeaderShown,
50-
disableAutoFixScroll,
51-
largeHeaderExists: !!LargeHeaderComponent,
52-
});
41+
const {
42+
scrollY,
43+
showNavBar,
44+
largeHeaderHeight,
45+
largeHeaderOpacity,
46+
scrollHandler,
47+
debouncedFixScroll,
48+
} = useScrollContainerLogic({
49+
scrollRef,
50+
largeHeaderShown,
51+
disableAutoFixScroll,
52+
largeHeaderExists: !!LargeHeaderComponent,
53+
});
5354

5455
return (
5556
<View
@@ -61,10 +62,16 @@ const AnimatedFlatListWithHeaders = <ItemT extends unknown>({
6162
]}
6263
>
6364
{HeaderComponent({ showNavBar })}
64-
<AnimatedFlatList
65-
ref={scrollRef}
65+
<Animated.FlatList
66+
ref={(_ref) => {
67+
// @ts-ignore
68+
scrollRef.current = _ref;
69+
// @ts-ignore
70+
if (ref) ref.current = _ref;
71+
}}
6672
scrollEventThrottle={16}
6773
overScrollMode="auto"
74+
onScroll={scrollHandler}
6875
automaticallyAdjustContentInsets={false}
6976
onScrollBeginDrag={(e) => {
7077
debouncedFixScroll.cancel();
@@ -103,6 +110,12 @@ const AnimatedFlatListWithHeaders = <ItemT extends unknown>({
103110
);
104111
};
105112

106-
export default AnimatedFlatListWithHeaders;
113+
// The typecast is needed to make the component generic.
114+
const FlatListWithHeaders = React.forwardRef(FlatListWithHeadersInputComp) as <ItemT = any>(
115+
props: AnimatedFlatListProps<ItemT> &
116+
SharedScrollContainerProps & { ref?: React.Ref<Animated.FlatList<ItemT>> }
117+
) => React.ReactElement;
118+
119+
export default FlatListWithHeaders;
107120

108121
const styles = StyleSheet.create({ container: { flex: 1 } });

src/components/containers/ScrollView.tsx

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { View, StyleSheet } from 'react-native';
33
import { useSafeAreaInsets } from 'react-native-safe-area-context';
4-
import Animated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
4+
import Animated, { useAnimatedRef } from 'react-native-reanimated';
55

66
import FadingView from './FadingView';
77
import { useScrollContainerLogic } from './useScrollContainerLogic';
@@ -11,37 +11,44 @@ type AnimatedScrollViewProps = React.ComponentProps<typeof Animated.ScrollView>
1111
children?: React.ReactNode;
1212
};
1313

14-
const AnimatedScrollViewWithHeaders: React.FC<
15-
AnimatedScrollViewProps & SharedScrollContainerProps
16-
> = ({
17-
largeHeaderShown,
18-
containerStyle,
19-
LargeHeaderComponent,
20-
largeHeaderContainerStyle,
21-
HeaderComponent,
22-
onLargeHeaderLayout,
23-
ignoreLeftSafeArea,
24-
ignoreRightSafeArea,
25-
onScrollBeginDrag,
26-
onScrollEndDrag,
27-
onMomentumScrollBegin,
28-
onMomentumScrollEnd,
29-
disableAutoFixScroll,
30-
children,
31-
...rest
32-
}) => {
14+
const ScrollViewWithHeadersInputComp = (
15+
{
16+
largeHeaderShown,
17+
containerStyle,
18+
LargeHeaderComponent,
19+
largeHeaderContainerStyle,
20+
HeaderComponent,
21+
onLargeHeaderLayout,
22+
ignoreLeftSafeArea,
23+
ignoreRightSafeArea,
24+
onScrollBeginDrag,
25+
onScrollEndDrag,
26+
onMomentumScrollBegin,
27+
onMomentumScrollEnd,
28+
disableAutoFixScroll,
29+
children,
30+
/** At the moment, we will not allow overriding of this since the scrollHandler needs it. */
31+
onScroll: _unusedOnScroll,
32+
...rest
33+
}: AnimatedScrollViewProps & SharedScrollContainerProps,
34+
ref: React.Ref<Animated.ScrollView>
35+
) => {
3336
const insets = useSafeAreaInsets();
3437
const scrollRef = useAnimatedRef<Animated.ScrollView>();
35-
const scrollY = useScrollViewOffset(scrollRef);
3638

37-
const { showNavBar, largeHeaderHeight, largeHeaderOpacity, debouncedFixScroll } =
38-
useScrollContainerLogic({
39-
scrollRef,
40-
scrollY,
41-
largeHeaderShown,
42-
disableAutoFixScroll,
43-
largeHeaderExists: !!LargeHeaderComponent,
44-
});
39+
const {
40+
scrollY,
41+
showNavBar,
42+
largeHeaderHeight,
43+
largeHeaderOpacity,
44+
scrollHandler,
45+
debouncedFixScroll,
46+
} = useScrollContainerLogic({
47+
scrollRef,
48+
largeHeaderShown,
49+
disableAutoFixScroll,
50+
largeHeaderExists: !!LargeHeaderComponent,
51+
});
4552

4653
return (
4754
<View
@@ -54,9 +61,15 @@ const AnimatedScrollViewWithHeaders: React.FC<
5461
>
5562
{HeaderComponent({ showNavBar })}
5663
<Animated.ScrollView
57-
ref={scrollRef}
64+
ref={(_ref) => {
65+
// @ts-ignore
66+
scrollRef.current = _ref;
67+
// @ts-ignore
68+
if (ref) ref.current = _ref;
69+
}}
5870
scrollEventThrottle={16}
5971
overScrollMode="auto"
72+
onScroll={scrollHandler}
6073
automaticallyAdjustContentInsets={false}
6174
onScrollBeginDrag={(e) => {
6275
debouncedFixScroll.cancel();
@@ -95,6 +108,11 @@ const AnimatedScrollViewWithHeaders: React.FC<
95108
);
96109
};
97110

98-
export default AnimatedScrollViewWithHeaders;
111+
const ScrollViewWithHeaders = React.forwardRef<
112+
Animated.ScrollView,
113+
AnimatedScrollViewProps & SharedScrollContainerProps
114+
>(ScrollViewWithHeadersInputComp);
115+
116+
export default ScrollViewWithHeaders;
99117

100118
const styles = StyleSheet.create({ container: { flex: 1 } });

src/components/containers/SectionList.tsx

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { View, SectionList, StyleSheet, SectionListProps } from 'react-native';
33
import { useSafeAreaInsets } from 'react-native-safe-area-context';
4-
import Animated, { useAnimatedRef, useScrollViewOffset } from 'react-native-reanimated';
4+
import Animated, { useAnimatedRef } from 'react-native-reanimated';
55

66
import FadingView from '../containers/FadingView';
77
import { useScrollContainerLogic } from './useScrollContainerLogic';
@@ -18,35 +18,43 @@ const AnimatedSectionList = Animated.createAnimatedComponent(SectionList) as Rea
1818
any
1919
>;
2020

21-
const AnimatedSectionListWithHeaders = <ItemT = any, SectionT = DefaultSectionT>({
22-
largeHeaderShown,
23-
containerStyle,
24-
LargeHeaderComponent,
25-
largeHeaderContainerStyle,
26-
HeaderComponent,
27-
onLargeHeaderLayout,
28-
onScrollBeginDrag,
29-
onScrollEndDrag,
30-
onMomentumScrollBegin,
31-
onMomentumScrollEnd,
32-
ignoreLeftSafeArea,
33-
ignoreRightSafeArea,
34-
disableAutoFixScroll,
35-
...rest
36-
}: AnimatedSectionListType<ItemT, SectionT>) => {
21+
const SectionListWithHeadersInputComp = <ItemT extends any = any, SectionT = DefaultSectionT>(
22+
{
23+
largeHeaderShown,
24+
containerStyle,
25+
LargeHeaderComponent,
26+
largeHeaderContainerStyle,
27+
HeaderComponent,
28+
onLargeHeaderLayout,
29+
onScrollBeginDrag,
30+
onScrollEndDrag,
31+
onMomentumScrollBegin,
32+
onMomentumScrollEnd,
33+
ignoreLeftSafeArea,
34+
ignoreRightSafeArea,
35+
disableAutoFixScroll,
36+
/** At the moment, we will not allow overriding of this since the scrollHandler needs it. */
37+
onScroll: _unusedOnScroll,
38+
...rest
39+
}: AnimatedSectionListType<ItemT, SectionT>,
40+
ref: React.Ref<Animated.ScrollView>
41+
) => {
3742
const insets = useSafeAreaInsets();
3843
const scrollRef = useAnimatedRef<any>();
39-
// Need to use `any` here because useScrollViewOffset is not typed for Animated.SectionList
40-
const scrollY = useScrollViewOffset(scrollRef as any);
4144

42-
const { showNavBar, largeHeaderHeight, largeHeaderOpacity, debouncedFixScroll } =
43-
useScrollContainerLogic({
44-
scrollRef,
45-
scrollY,
46-
largeHeaderShown,
47-
disableAutoFixScroll,
48-
largeHeaderExists: !!LargeHeaderComponent,
49-
});
45+
const {
46+
scrollY,
47+
showNavBar,
48+
largeHeaderHeight,
49+
largeHeaderOpacity,
50+
scrollHandler,
51+
debouncedFixScroll,
52+
} = useScrollContainerLogic({
53+
scrollRef,
54+
largeHeaderShown,
55+
disableAutoFixScroll,
56+
largeHeaderExists: !!LargeHeaderComponent,
57+
});
5058

5159
return (
5260
<View
@@ -59,9 +67,15 @@ const AnimatedSectionListWithHeaders = <ItemT = any, SectionT = DefaultSectionT>
5967
>
6068
{HeaderComponent({ showNavBar })}
6169
<AnimatedSectionList
62-
ref={scrollRef}
70+
ref={(_ref) => {
71+
// @ts-ignore
72+
scrollRef.current = _ref;
73+
// @ts-ignore
74+
if (ref) ref.current = _ref;
75+
}}
6376
scrollEventThrottle={16}
6477
overScrollMode="auto"
78+
onScroll={scrollHandler}
6579
automaticallyAdjustContentInsets={false}
6680
onScrollBeginDrag={(e) => {
6781
debouncedFixScroll.cancel();
@@ -100,6 +114,14 @@ const AnimatedSectionListWithHeaders = <ItemT = any, SectionT = DefaultSectionT>
100114
);
101115
};
102116

103-
export default AnimatedSectionListWithHeaders;
117+
// The typecast is needed to make the component generic.
118+
const SectionListWithHeaders = React.forwardRef(SectionListWithHeadersInputComp) as <
119+
ItemT = any,
120+
SectionT = DefaultSectionT
121+
>(
122+
props: AnimatedSectionListType<ItemT, SectionT> & { ref?: React.Ref<Animated.ScrollView> }
123+
) => React.ReactElement;
124+
125+
export default SectionListWithHeaders;
104126

105127
const styles = StyleSheet.create({ container: { flex: 1 } });

src/components/containers/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,4 +113,9 @@ export type SharedScrollContainerProps = {
113113
* Fires when scroll view has finished moving.
114114
*/
115115
onMomentumScrollEnd?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;
116+
/**
117+
* This property is not supported at the moment. If you would like to listen to
118+
* scroll events, use the useScrollViewOffset hook with a ref.
119+
*/
120+
onScroll?: React.ComponentProps<typeof Animated.ScrollView>['onScroll'];
116121
};

0 commit comments

Comments
 (0)