ScrollView working wrong
See original GitHub issueDescription
I try to reproduce bottom-sheet like this: https://github.com/software-mansion/react-native-gesture-handler/blob/main/example/src/new_api/bottom_sheet/index.tsx , but using reanimted api and functional component. But I faced this problem, scrollview not scrolling in first snap, but, when snap changes, scrollview will constantly scroll Please help, I don’t understand how this interaction should happen
Platforms
- iOS
- Android
- Web
Video
Steps To Reproduce
Just use below code
Snack or minimal code example
import React, {
createRef,
forwardRef,
PropsWithChildren,
useCallback,
useImperativeHandle,
useMemo,
useState,
} from 'react';
import { Dimensions, View } from 'react-native';
import {
GestureHandlerRootView,
NativeViewGestureHandler,
PanGestureHandler,
PanGestureHandlerGestureEvent,
TapGestureHandler,
} from 'react-native-gesture-handler';
import Animated, {
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withSpring,
} from 'react-native-reanimated';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
const DAMPING_COEFFICIENT = 20;
const { height: HEIGHT } = Dimensions.get('window');
const useSheetPosition = () => {
const { top } = useSafeAreaInsets();
return useMemo(
() => ({
startSnap: HEIGHT / 2,
hideSnap: HEIGHT / 1.5,
maxSnap: top,
}),
[top],
);
};
interface SheetProps {
onOpened?: (state: boolean) => void;
}
export interface SheetRefProps {
setCollapse: (state: boolean) => void;
}
const Sheet = forwardRef<PropsWithChildren<SheetRefProps>, SheetProps>(
({ children, onOpened }, ref) => {
const { top } = useSafeAreaInsets();
const { startSnap, maxSnap, hideSnap } = useSheetPosition();
const [currentSnap, setCurrentSnap] = useState(startSnap);
const scrollOffset = useSharedValue(0);
const opacity = useSharedValue(1);
const translateY = useSharedValue(startSnap);
const tapMainRef = createRef();
const panHeaderRef = createRef();
const nativeScrollRef = createRef();
const panContainerRef = createRef();
const transformTo = useCallback(
(destination: number) => {
'worklet';
translateY.value = withSpring(destination, { mass: 0.5 });
},
[translateY],
);
const setCollapse = useCallback(
state => {
if (state) {
transformTo(hideSnap);
opacity.value = 0.8;
} else {
transformTo(startSnap);
opacity.value = 1;
}
},
[hideSnap, opacity, startSnap, transformTo],
);
useImperativeHandle(ref, () => ({ setCollapse }), [setCollapse]);
const onHandlerEndOnJS = ({ open, snap }) => {
if (onOpened) onOpened(open);
setCurrentSnap(snap);
};
const gestureHandler = useAnimatedGestureHandler<
PanGestureHandlerGestureEvent,
{ startY: number }
>({
onStart: (_, ctx) => {
ctx.startY = translateY.value;
},
onActive: (event, ctx) => {
if (
(translateY.value > top - DAMPING_COEFFICIENT && translateY.value <= startSnap) ||
(translateY.value > startSnap && translateY.value < startSnap + DAMPING_COEFFICIENT)
) {
translateY.value = event.translationY + ctx.startY;
}
},
onEnd: () => {
if (translateY.value < HEIGHT / 3) {
transformTo(maxSnap);
runOnJS(onHandlerEndOnJS)({
open: true,
snap: maxSnap,
});
} else {
transformTo(startSnap);
runOnJS(onHandlerEndOnJS)({
open: false,
snap: startSnap,
});
}
},
});
const sheetAnimatedStyle = useAnimatedStyle(() => {
return {
transform: [{ translateY: translateY.value }],
opacity: opacity.value,
};
});
return (
<View
style={{ flex: 1, position: 'absolute', top: 0, zIndex: 11, left: 0, right: 0 }}
pointerEvents="box-none"
>
<GestureHandlerRootView style={{ flex: 1 }}>
<TapGestureHandler
ref={tapMainRef}
maxDurationMs={100000}
maxDeltaY={currentSnap - maxSnap}
>
<Animated.View style={[styles.container, sheetAnimatedStyle]}>
<PanGestureHandler
ref={panHeaderRef}
simultaneousHandlers={[nativeScrollRef, tapMainRef]}
shouldCancelWhenOutside={false}
enableTrackpadTwoFingerGesture
onGestureEvent={gestureHandler}
>
<Animated.View style={styles.handle} />
</PanGestureHandler>
<PanGestureHandler
ref={panContainerRef}
simultaneousHandlers={[nativeScrollRef, tapMainRef]}
shouldCancelWhenOutside={false}
onGestureEvent={gestureHandler}
enableTrackpadTwoFingerGesture
>
<Animated.View style={styles.innerContainer}>
<NativeViewGestureHandler
ref={nativeScrollRef}
waitFor={tapMainRef}
simultaneousHandlers={panContainerRef}
>
<Animated.ScrollView
style={styles.scrollContainer}
bounces={false}
scrollEventThrottle={1}
onScrollBeginDrag={e => {
scrollOffset.value = e.nativeEvent.contentOffset.y;
}}
>
{children}
</Animated.ScrollView>
</NativeViewGestureHandler>
</Animated.View>
</PanGestureHandler>
</Animated.View>
</TapGestureHandler>
</GestureHandlerRootView>
</View>
);
},
);
const styles = StyleSheet.create({
container: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
width: '100%',
height: HEIGHT,
paddingBottom: 48,
backgroundColor: '#fff',
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
},
handle: {
height: 30,
background: '#f0f0f0',
}
innerContainer: {
flex: 1,
},
scrollContainer: {
flex: 1,
maxHeight: '100%',
},
});
export { Sheet };
Expected behavior
Work right, like bottom sheet
Actual behavior
Scrollview scrolling always after change state for TapGestureHandler
Package versions
- React: 17.0.2
- React Native: 0.67.4
- React Native Gesture Handler: ^2.3.2
- React Native Reanimated: ^2.4.1
Issue Analytics
- State:
- Created a year ago
- Comments:6 (4 by maintainers)
Top Results From Across the Web
Android ScrollView not working properly - Stack Overflow
I developed an android application in which the scroll-view is not scrolling.. I am posting the code here pls check and if found...
Read more >Common bugs in React Native ScrollView and how to fix them
React Native's ScrollView component is ubiquitous, but its implementation can sometimes lead to mistakes. Learn what to look out for here.
Read more >ScrollView won't scroll | Apple Developer Forums
I have successfully created a scrollview with several buttons that stack on top of one another. But the View won't scroll. What am...
Read more >ScrollView - React Native
Component that wraps platform ScrollView while providing ... Only works for vertical ScrollViews ( horizontal prop must be false ).
Read more >ScrollView - Android Developers
For vertical scrolling, consider NestedScrollView instead of scroll view ... android:saveEnabled, If false, no state will be saved for this view when it...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I took a more in-depth look at your code, here are some tips to get it to work:
useRef
instead ofcreateRef
in functional components,createRef
will return a new object every render whileuseRef
will return the same onecontentOffset.y
is not zero)Here’s the updated code to show what I mean:
Happy to see you have solved the problem.