From ab8dc49095dcad6860995eb2a963e8225a1cbc2f Mon Sep 17 00:00:00 2001 From: Alexis CHAPPRON Date: Sat, 30 Mar 2024 14:19:48 +0100 Subject: [PATCH] feat: suggest useComposedScrollHandler hook The goal of this hook is to be able to compose multiple scrollHandler, and therefore "hide" the fact that a custom scrollView already use a scrollHandler. --- .../examples/ComposedScrollHandler/index.tsx | 54 +++++++++++++++++++ app/src/examples/index.ts | 6 +++ .../hook/composeAnimatedScrollHandler.ts | 28 ++++++++++ 3 files changed, 88 insertions(+) create mode 100644 app/src/examples/ComposedScrollHandler/index.tsx create mode 100644 src/reanimated2/hook/composeAnimatedScrollHandler.ts diff --git a/app/src/examples/ComposedScrollHandler/index.tsx b/app/src/examples/ComposedScrollHandler/index.tsx new file mode 100644 index 000000000000..27db7f3c95a5 --- /dev/null +++ b/app/src/examples/ComposedScrollHandler/index.tsx @@ -0,0 +1,54 @@ +import Animated, { + AnimatedScrollViewProps, + useAnimatedScrollHandler, +} from 'react-native-reanimated'; +import { StyleSheet, Text } from 'react-native'; + +import React from 'react'; +import { useComposedScrollHandler } from '../../../../src/reanimated2/hook/composeAnimatedScrollHandler'; + +export default function ComposedAnimatedScrollHandlerExample() { + const externalScrollHandler = useAnimatedScrollHandler((event) => { + console.log('External handler', event.contentOffset.y); + }); + + return ( + + {[...Array(100)].map((_, i) => ( + + {i} + + ))} + + ); +} + +function CustomScrollView(props: AnimatedScrollViewProps) { + const scrollHandler = useAnimatedScrollHandler((event) => { + console.log('Internal handler', event.contentOffset.y); + }); + + const composedScrollHandler = useComposedScrollHandler([ + scrollHandler, + props.onScroll, // <- I don't know how to correctly type this + ]); + + return ( + + {props.children} + + ); +} + +const styles = StyleSheet.create({ + scrollView: { + flex: 1, + width: '100%', + }, + text: { + fontSize: 50, + textAlign: 'center', + }, +}); diff --git a/app/src/examples/index.ts b/app/src/examples/index.ts index c93d190e6d88..8184e3c7805c 100644 --- a/app/src/examples/index.ts +++ b/app/src/examples/index.ts @@ -124,6 +124,7 @@ import HabitsExample from './LayoutAnimations/HabitsExample'; import MemoExample from './MemoExample'; import PerformanceMonitorExample from './PerfomanceMonitorExample'; import ScreenTransitionExample from './ScreenTransitionExample'; +import ComposedScrollHandler from './ComposedScrollHandler'; interface Example { icon?: string; @@ -341,6 +342,11 @@ export const EXAMPLES: Record = { title: 'useFrameCallback', screen: FrameCallbackExample, }, + ComposedScrollHandler: { + icon: '🔀', + title: 'useComposedScrollHandler', + screen: ComposedScrollHandler, + }, ScrollViewExample: { icon: '📜', title: 'useAnimatedScrollHandler', diff --git a/src/reanimated2/hook/composeAnimatedScrollHandler.ts b/src/reanimated2/hook/composeAnimatedScrollHandler.ts new file mode 100644 index 000000000000..996b7dc5d3c0 --- /dev/null +++ b/src/reanimated2/hook/composeAnimatedScrollHandler.ts @@ -0,0 +1,28 @@ +import type {ScrollHandlerInternal, ScrollHandlerProcessed} from "./useAnimatedScrollHandler"; +import {useEvent, useHandler} from "./index"; +import type {RNNativeScrollEvent} from "./commonTypes"; + + + +export function useComposedScrollHandler>( + handlerList: Array | undefined>, // <- I don't know how to correctly type this +){ + + /** + * unknown casting seems legitimate. See {@link useHandler} override + */ + const internalHandlerList = handlerList as unknown as Array; + + const {doDependenciesDiffer} = useHandler({}, internalHandlerList); + + const allWorklets = internalHandlerList.flatMap(handler => handler.workletEventHandler?.worklet ? handler.workletEventHandler?.worklet : []); + + return useEvent( + (event) => { + 'worklet'; + allWorklets.forEach(it => it(event)); + }, + ['onScroll', 'onScrollBeginDrag', 'onScrollEndDrag', 'onMomentumScrollBegin', 'onMomentumScrollEnd'], + doDependenciesDiffer, + ); +}