UNPKG

react-native-gesture-handler

Version:

Declarative API exposing native platform touch and gesture system to React Native

118 lines (107 loc) 4.68 kB
/* eslint-disable react/no-unused-prop-types */ import React, { useContext, useEffect, useLayoutEffect, useMemo, useRef } from 'react'; import { Platform } from 'react-native'; import findNodeHandle from '../../../findNodeHandle'; import { isJestEnv } from '../../../utils'; import GestureHandlerRootViewContext from '../../../GestureHandlerRootViewContext'; import { useAnimatedGesture } from './useAnimatedGesture'; import { attachHandlers } from './attachHandlers'; import { needsToReattach } from './needsToReattach'; import { dropHandlers } from './dropHandlers'; import { useWebEventHandlers } from './utils'; import { Wrap, AnimatedWrap } from './Wrap'; import { useDetectorUpdater } from './useDetectorUpdater'; import { useViewRefHandler } from './useViewRefHandler'; import { useMountReactions } from './useMountReactions'; function propagateDetectorConfig(props, gesture) { const keysToPropagate = ['userSelect', 'enableContextMenu', 'touchAction']; for (const key of keysToPropagate) { const value = props[key]; if (value === undefined) { continue; } for (const g of gesture.toGestureArray()) { const config = g.config; config[key] = value; } } } /** * `GestureDetector` is responsible for creating and updating native gesture handlers based on the config of provided gesture. * * ### Props * - `gesture` * - `userSelect` (**Web only**) * - `enableContextMenu` (**Web only**) * - `touchAction` (**Web only**) * * ### Remarks * - Gesture Detector will use first native view in its subtree to recognize gestures, however if this view is used only to group its children it may get automatically collapsed. * - Using the same instance of a gesture across multiple Gesture Detectors is not possible. * * @see https://docs.swmansion.com/react-native-gesture-handler/docs/gestures/gesture-detector */ export const GestureDetector = props => { const rootViewContext = useContext(GestureHandlerRootViewContext); if (__DEV__ && !rootViewContext && !isJestEnv() && Platform.OS !== 'web') { throw new Error('GestureDetector must be used as a descendant of GestureHandlerRootView. Otherwise the gestures will not be recognized. See https://docs.swmansion.com/react-native-gesture-handler/docs/installation for more details.'); } // Gesture config should be wrapped with useMemo to prevent unnecessary re-renders const gestureConfig = props.gesture; propagateDetectorConfig(props, gestureConfig); const gesturesToAttach = useMemo(() => gestureConfig.toGestureArray(), [gestureConfig]); const shouldUseReanimated = gesturesToAttach.some(g => g.shouldUseReanimated); const webEventHandlersRef = useWebEventHandlers(); // Store state in ref to prevent unnecessary renders const state = useRef({ firstRender: true, viewRef: null, previousViewTag: -1, forceRebuildReanimatedEvent: false }).current; const preparedGesture = React.useRef({ attachedGestures: [], animatedEventHandler: null, animatedHandlers: null, shouldUseReanimated: shouldUseReanimated, isMounted: false }).current; const updateAttachedGestures = useDetectorUpdater(state, preparedGesture, gesturesToAttach, gestureConfig, webEventHandlersRef); const refHandler = useViewRefHandler(state, updateAttachedGestures); // Reanimated event should be rebuilt only when gestures are reattached, otherwise // config update will be enough as all necessary items are stored in shared values anyway const needsToRebuildReanimatedEvent = state.firstRender || state.forceRebuildReanimatedEvent || needsToReattach(preparedGesture, gesturesToAttach); state.forceRebuildReanimatedEvent = false; useAnimatedGesture(preparedGesture, needsToRebuildReanimatedEvent); useLayoutEffect(() => { const viewTag = findNodeHandle(state.viewRef); preparedGesture.isMounted = true; attachHandlers({ preparedGesture, gestureConfig, gesturesToAttach, webEventHandlersRef, viewTag }); return () => { preparedGesture.isMounted = false; dropHandlers(preparedGesture); }; }, []); useEffect(() => { if (state.firstRender) { state.firstRender = false; } else { updateAttachedGestures(); } }, [props]); useMountReactions(updateAttachedGestures, preparedGesture); if (shouldUseReanimated) { return /*#__PURE__*/React.createElement(AnimatedWrap, { ref: refHandler, onGestureHandlerEvent: preparedGesture.animatedEventHandler }, props.children); } else { return /*#__PURE__*/React.createElement(Wrap, { ref: refHandler }, props.children); } }; //# sourceMappingURL=index.js.map