Skip to content

Commit

Permalink
chore: introduce focus hook to reset scrollable ref
Browse files Browse the repository at this point in the history
  • Loading branch information
gorhom committed Jul 31, 2020
1 parent 0a7ed41 commit 70061ec
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 60 deletions.
8 changes: 4 additions & 4 deletions example/src/screens/FlatListExample.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback, useMemo, useRef } from 'react';
import { View, StyleSheet } from 'react-native';
import { useHeaderHeight } from '@react-navigation/stack';
import ScrollBottomSheet, { FlatList } from '@gorhom/bottom-sheet';
import BottomSheet, { FlatList } from '@gorhom/bottom-sheet';
import { useSafeArea } from 'react-native-safe-area-context';
import Handle from '../components/Handle';
import Button from '../components/button';
Expand All @@ -10,7 +10,7 @@ import { createContactListMockData } from '../utils';

const FlatListExample = () => {
// hooks
const sheetRef = useRef<ScrollBottomSheet>(null);
const sheetRef = useRef<BottomSheet>(null);
const { bottom: bottomSafeArea } = useSafeArea();
const headerHeight = useHeaderHeight();

Expand Down Expand Up @@ -57,7 +57,7 @@ const FlatListExample = () => {
style={styles.buttonContainer}
onPress={() => handleSnapPress(2)}
/>
<ScrollBottomSheet
<BottomSheet
ref={sheetRef}
snapPoints={snapPoints}
initialSnapIndex={2}
Expand All @@ -70,7 +70,7 @@ const FlatListExample = () => {
renderItem={renderItem}
contentContainerStyle={contentContainerStyle}
/>
</ScrollBottomSheet>
</BottomSheet>
</View>
);
};
Expand Down
8 changes: 4 additions & 4 deletions example/src/screens/ScrollViewExample.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback, useMemo, useRef } from 'react';
import { View, StyleSheet } from 'react-native';
import { useHeaderHeight } from '@react-navigation/stack';
import ScrollBottomSheet, { ScrollView } from '@gorhom/bottom-sheet';
import BottomSheet, { ScrollView } from '@gorhom/bottom-sheet';
import { useSafeArea } from 'react-native-safe-area-context';
import Handle from '../components/Handle';
import Button from '../components/button';
Expand All @@ -10,7 +10,7 @@ import { createContactListMockData } from '../utils';

const ScrollViewExample = () => {
// hooks
const sheetRef = useRef<ScrollBottomSheet>(null);
const sheetRef = useRef<BottomSheet>(null);
const { bottom: bottomSafeArea } = useSafeArea();
const headerHeight = useHeaderHeight();

Expand Down Expand Up @@ -57,7 +57,7 @@ const ScrollViewExample = () => {
style={styles.buttonContainer}
onPress={() => handleSnapPress(2)}
/>
<ScrollBottomSheet
<BottomSheet
ref={sheetRef}
snapPoints={snapPoints}
initialSnapIndex={2}
Expand All @@ -67,7 +67,7 @@ const ScrollViewExample = () => {
<ScrollView contentContainerStyle={contentContainerStyle}>
{data.map(renderItem)}
</ScrollView>
</ScrollBottomSheet>
</BottomSheet>
</View>
);
};
Expand Down
8 changes: 4 additions & 4 deletions example/src/screens/SectionListExample.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback, useMemo, useRef } from 'react';
import { Platform, StyleSheet, Text, View } from 'react-native';
import { useHeaderHeight } from '@react-navigation/stack';
import ScrollBottomSheet, { SectionList } from '@gorhom/bottom-sheet';
import BottomSheet, { SectionList } from '@gorhom/bottom-sheet';
import { useSafeArea } from 'react-native-safe-area-context';
import Handle from '../components/Handle';
import Button from '../components/button';
Expand All @@ -10,7 +10,7 @@ import { createContactSectionsMockData } from '../utils';

const SectionListExample = () => {
// hooks
const sheetRef = useRef<ScrollBottomSheet>(null);
const sheetRef = useRef<BottomSheet>(null);
const { bottom: bottomSafeArea } = useSafeArea();
const headerHeight = useHeaderHeight();

Expand Down Expand Up @@ -68,7 +68,7 @@ const SectionListExample = () => {
style={styles.buttonContainer}
onPress={() => handleSnapPress(2)}
/>
<ScrollBottomSheet
<BottomSheet
ref={sheetRef}
snapPoints={snapPoints}
initialSnapIndex={2}
Expand All @@ -86,7 +86,7 @@ const SectionListExample = () => {
Platform.OS === 'android' && sections.length > 0
}
/>
</ScrollBottomSheet>
</BottomSheet>
</View>
);
};
Expand Down
11 changes: 5 additions & 6 deletions src/components/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ import Animated, {
Value,
} from 'react-native-reanimated';
import {
NativeViewGestureHandler,
PanGestureHandler,
PanGestureHandlerProperties,
State as GestureState,
TapGestureHandler,
PanGestureHandlerGestureEvent,
NativeViewGestureHandler,
} from 'react-native-gesture-handler';
import { BottomSheetInternalProvider } from '../../context';
import { Scrollable, ScrollableRef } from '../../types';
Expand Down Expand Up @@ -155,7 +155,6 @@ export class BottomSheet extends Component<BottomSheetProps> {
private drawerHandleRef = React.createRef<PanGestureHandler>();
private drawerContentRef = React.createRef<PanGestureHandler>();
private scrollComponentRef = React.createRef<NativeViewGestureHandler>();

private scrollableRef = React.createRef<ScrollableRef>();

/**
Expand All @@ -167,8 +166,8 @@ export class BottomSheet extends Component<BottomSheetProps> {
/**
* Pan gesture handler events for drawer handle and content
*/
private onHandleGestureEvent: PanGestureHandlerProperties['onGestureEvent'];
private onDrawerGestureEvent: PanGestureHandlerProperties['onGestureEvent'];
private onHandleGestureEvent: (event: PanGestureHandlerGestureEvent) => void;
private onDrawerGestureEvent: (event: PanGestureHandlerGestureEvent) => void;
/**
* Main Animated Value that drives the top position of the UI drawer at any point in time
*/
Expand Down Expand Up @@ -615,9 +614,9 @@ export class BottomSheet extends Component<BottomSheetProps> {
<Animated.View style={styles.container}>
<BottomSheetInternalProvider
value={{
scrollComponentRef: this.scrollComponentRef,
masterDrawerRef: this.masterDrawer,
drawerContentRef: this.drawerContentRef,
scrollComponentRef: this.scrollComponentRef,
decelerationRate: this.decelerationRate,
contentPaddingBottom: this.getNormalisedSnapPoints()[0],
setScrollableRef: this.setScrollableRef,
Expand Down
24 changes: 10 additions & 14 deletions src/components/flatList/FlatList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import Reanimated from 'react-native-reanimated';
import { NativeViewGestureHandler } from 'react-native-gesture-handler';
import { useBottomSheetInternal } from '../../hooks';
import { BottomSheetFlatListProps, BottomSheetFlatList } from './types';

const AnimatedFlatList = Reanimated.createAnimatedComponent(
RNFlatList
Expand All @@ -22,28 +23,23 @@ const AnimatedFlatList = Reanimated.createAnimatedComponent(
any
>;

type FlatListProps<T> = Omit<
RNFlatListProps<T>,
| 'overScrollMode'
| 'bounces'
| 'decelerationRate'
| 'onScrollBeginDrag'
| 'scrollEventThrottle'
>;

const FlatList = forwardRef(
(props: FlatListProps<any>, ref: Ref<RNFlatList>) => {
(props: BottomSheetFlatListProps<any>, ref: Ref<RNFlatList>) => {
// props
const { contentContainerStyle: _contentContainerStyle, ...rest } = props;
const {
contentContainerStyle: _contentContainerStyle,
focusHook: useFocusHook = useEffect,
...rest
} = props;

// refs
const flatListRef = useRef<RNFlatList>(null);

// hooks
const {
scrollComponentRef,
masterDrawerRef,
drawerContentRef,
scrollComponentRef,
decelerationRate,
contentPaddingBottom,
setScrollableRef,
Expand All @@ -65,7 +61,7 @@ const FlatList = forwardRef(
// effects
// @ts-ignore
useImperativeHandle(ref, () => flatListRef.current!.getNode());
useEffect(() => {
useFocusHook(() => {
setScrollableRef(flatListRef);
return () => {
removeScrollableRef(flatListRef);
Expand Down Expand Up @@ -94,4 +90,4 @@ const FlatList = forwardRef(
}
);

export default (FlatList as any) as typeof RNFlatList;
export default (FlatList as any) as typeof BottomSheetFlatList;
83 changes: 83 additions & 0 deletions src/components/flatList/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import type { Component, EffectCallback, DependencyList } from 'react';
import type { FlatListProps as RNFlatListProps } from 'react-native';

type BottomSheetFlatListProps<T> = Omit<
RNFlatListProps<T>,
| 'overScrollMode'
| 'bounces'
| 'decelerationRate'
| 'onScrollBeginDrag'
| 'scrollEventThrottle'
> & {
focusHook?: (effect: EffectCallback, deps?: DependencyList) => void;
};

export class BottomSheetFlatList<T = any> extends Component<
BottomSheetFlatListProps<T>
> {
/**
* Scrolls to the end of the content. May be janky without `getItemLayout` prop.
*/
scrollToEnd: (params?: { animated?: boolean | null }) => void;

/**
* Scrolls to the item at the specified index such that it is positioned in the viewable area
* such that viewPosition 0 places it at the top, 1 at the bottom, and 0.5 centered in the middle.
* Cannot scroll to locations outside the render window without specifying the getItemLayout prop.
*/
scrollToIndex: (params: {
animated?: boolean | null;
index: number;
viewOffset?: number;
viewPosition?: number;
}) => void;

/**
* Requires linear scan through data - use `scrollToIndex` instead if possible.
* May be janky without `getItemLayout` prop.
*/
scrollToItem: (params: {
animated?: boolean | null;
item: ItemT;
viewPosition?: number;
}) => void;

/**
* Scroll to a specific content pixel offset, like a normal `ScrollView`.
*/
scrollToOffset: (params: {
animated?: boolean | null;
offset: number;
}) => void;

/**
* Tells the list an interaction has occured, which should trigger viewability calculations,
* e.g. if waitForInteractions is true and the user has not scrolled. This is typically called
* by taps on items or by navigation actions.
*/
recordInteraction: () => void;

/**
* Displays the scroll indicators momentarily.
*/
flashScrollIndicators: () => void;

/**
* Provides a handle to the underlying scroll responder.
*/
getScrollResponder: () => JSX.Element | null | undefined;

/**
* Provides a reference to the underlying host component
*/
getNativeScrollRef: () =>
| React.RefObject<View>
| React.RefObject<ScrollViewComponent>
| null
| undefined;

getScrollableNode: () => any;

// TODO: use `unknown` instead of `any` for Typescript >= 3.0
setNativeProps: (props: { [key: string]: any }) => void;
}
24 changes: 9 additions & 15 deletions src/components/scrollView/ScrollView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
import Reanimated from 'react-native-reanimated';
import { NativeViewGestureHandler } from 'react-native-gesture-handler';
import { useBottomSheetInternal } from '../../hooks';
import { BottomSheetScrollView, BottomSheetScrollViewProps } from './types';

const AnimatedScrollView = Reanimated.createAnimatedComponent(
RNScrollView
Expand All @@ -22,21 +23,14 @@ const AnimatedScrollView = Reanimated.createAnimatedComponent(
any
>;

type ScrollViewProps = Omit<
RNScrollViewProps,
| 'overScrollMode'
| 'bounces'
| 'decelerationRate'
| 'onScrollBeginDrag'
| 'scrollEventThrottle'
> & {
children: React.ReactNode[] | React.ReactNode;
};

const ScrollView = forwardRef(
(props: ScrollViewProps, ref: Ref<RNScrollView>) => {
(props: BottomSheetScrollViewProps, ref: Ref<RNScrollView>) => {
// props
const { contentContainerStyle: _contentContainerStyle, ...rest } = props;
const {
contentContainerStyle: _contentContainerStyle,
focusHook: useFocusHook = useEffect,
...rest
} = props;

// refs
const scrollViewRef = useRef<RNScrollView>(null);
Expand Down Expand Up @@ -67,7 +61,7 @@ const ScrollView = forwardRef(
// effects
// @ts-ignore
useImperativeHandle(ref, () => scrollViewRef.current!.getNode());
useEffect(() => {
useFocusHook(() => {
setScrollableRef(scrollViewRef);
return () => {
removeScrollableRef(scrollViewRef);
Expand Down Expand Up @@ -95,4 +89,4 @@ const ScrollView = forwardRef(
}
);

export default ScrollView;
export default (ScrollView as any) as typeof BottomSheetScrollView;
Loading

0 comments on commit 70061ec

Please sign in to comment.