react-native-gesture-handler
Version:
Declarative API exposing native platform touch and gesture system to React Native
118 lines (107 loc) • 4.68 kB
JavaScript
/* 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