Skip to content

Instantly share code, notes, and snippets.

@asgvard
Created March 2, 2018 12:07
Show Gist options
  • Save asgvard/7b671cca428470f2eee8c17861905e03 to your computer and use it in GitHub Desktop.
Save asgvard/7b671cca428470f2eee8c17861905e03 to your computer and use it in GitHub Desktop.
Root responder for global interception of touch events in RN
/**
* Wrapper for creating Global (RootContainer) PanResponder
* Responder events are bubbled up by the View hierarchy, but sometimes it's needed to "move"
* touch handling to a sibling component. Instead we're creating Root Responder to capture the touch and
* delegate the callbacks to any component that wants to listen to it.
*
* Any component in the App can hook into the Root Responder lifecycle callbacks
* For capturing of the Root Responder the *Capture callbacks should return true
*
* Only one component at a time can be attached to Root Responder
* As soon as another one hooks into the callbacks, the old component will stop receiving events
*/
import {PanResponder} from 'react-native';
import {isString, isFunction} from 'lodash';
/**
* Default callback always returns false so none of touch events gets captured
*/
const defaultCallback = () => false;
let onStartShouldSetPanResponder = defaultCallback;
let onMoveShouldSetPanResponder = defaultCallback;
let onPanResponderMove = defaultCallback;
let onPanResponderRelease = defaultCallback;
export const CALLBACK_TYPES = {
ON_START_CAPTURE: 'ON_START_CAPTURE',
ON_MOVE_CAPTURE: 'ON_MOVE_CAPTURE',
ON_MOVE: 'ON_MOVE',
ON_RELEASE: 'ON_RELEASE'
};
/**
* Every callback needs to be wrapped into an arrow function to keep reference in PanResponder
* But still to be able to swap the actual callback on a fly
*/
const panResponder = PanResponder.create({
onStartShouldSetPanResponderCapture: (event, gestureState) => onStartShouldSetPanResponder(event, gestureState),
onMoveShouldSetPanResponderCapture: (event, gestureState) => onMoveShouldSetPanResponder(event, gestureState),
onPanResponderMove: (event, gestureState) => onPanResponderMove(event, gestureState),
onPanResponderRelease: (event, gestureState) => onPanResponderRelease(event, gestureState),
onPanResponderTerminate: (event, gestureState) => onPanResponderRelease(event, gestureState)
});
export const getPanHandlers = () => panResponder.panHandlers;
export const setCallback = (callback, type) => {
if (!isString(type)) {
throw new Error(`Root PanResponder callback type is not a String: ${JSON.stringify(type)}`);
}
if (!isFunction(callback)) {
throw new Error('Root PanResponder callback is not a Function');
}
switch (type) {
case CALLBACK_TYPES.ON_START_CAPTURE:
onStartShouldSetPanResponder = callback;
break;
case CALLBACK_TYPES.ON_MOVE_CAPTURE:
onMoveShouldSetPanResponder = callback;
break;
case CALLBACK_TYPES.ON_MOVE:
onPanResponderMove = callback;
break;
case CALLBACK_TYPES.ON_RELEASE:
onPanResponderRelease = callback;
break;
default:
break;
}
};
export const clearAllCallbacks = () => {
onStartShouldSetPanResponder = defaultCallback;
onMoveShouldSetPanResponder = defaultCallback;
onPanResponderMove = defaultCallback;
onPanResponderRelease = defaultCallback;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment