react-native-keyboard-controller
Version:
Keyboard manager which works in identical way on both iOS and Android
109 lines (93 loc) • 3.45 kB
text/typescript
import { useRef } from "react";
import { Animated } from "react-native";
import { findNodeHandle } from "./utils/findNodeHandle";
import type { EventHandlerProcessed } from "react-native-reanimated";
type ComponentOrHandle = Parameters<typeof findNodeHandle>[0];
type WorkletHandler = {
registerForEvents: (viewTag: number) => void;
unregisterFromEvents: (viewTag: number) => void;
};
type WorkletHandlerOrWorkletHandlerObject =
| WorkletHandler
| {
workletEventHandler: WorkletHandler;
};
/**
* An internal hook that helps to register workletized event handlers.
*
* @param viewTagRef - Ref to the view that produces events.
* @returns A function that registers supplied event handlers.
* @example
* ```ts
* const setKeyboardHandlers = useEventHandlerRegistration<KeyboardHandler>(
* keyboardEventsMap,
* viewTagRef,
* );
* ```
*/
export function useEventHandlerRegistration(
viewTagRef: React.MutableRefObject<ComponentOrHandle>,
) {
const onRegisterHandler = (handler: EventHandlerProcessed<never, never>) => {
const currentHandler =
handler as unknown as WorkletHandlerOrWorkletHandlerObject;
const attachWorkletHandlers = () => {
const viewTag = findNodeHandle(viewTagRef.current);
if (__DEV__ && !viewTag) {
console.warn(
"Can not attach worklet handlers for `react-native-keyboard-controller` because view tag can not be resolved. Be sure that `KeyboardProvider` is fully mounted before registering handlers. If you think it is a bug in library, please open an issue.",
);
}
if (viewTag) {
if ("workletEventHandler" in currentHandler) {
currentHandler.workletEventHandler.registerForEvents(viewTag);
} else {
currentHandler.registerForEvents(viewTag);
}
}
};
if (viewTagRef.current) {
attachWorkletHandlers();
} else {
// view may not be mounted yet - defer registration until call-stack becomes empty
queueMicrotask(attachWorkletHandlers);
}
return () => {
const viewTag = findNodeHandle(viewTagRef.current);
if (viewTag) {
if ("workletEventHandler" in currentHandler) {
currentHandler.workletEventHandler.unregisterFromEvents(viewTag);
} else {
currentHandler.unregisterFromEvents(viewTag);
}
}
};
};
return onRegisterHandler;
}
/**
* TS variant of `useAnimatedValue` hook which is added in RN 0.71
* A better alternative of storing animated values in refs, since
* it doesn't recreate a new `Animated.Value` object on every re-render
* and therefore consumes less memory. We can not use a variant from
* RN, since this library supports earlier versions of RN.
*
* @param initialValue - Initial value of the animated value (numeric).
* @param config - Additional {@link Animated.AnimatedConfig|configuration} for the animated value.
* @returns Properly memoized {@link Animated.Value|Animated} value.
* @see https://github.com/facebook/react-native/commit/e22217fe8b9455e32695f88ca835e11442b0a937
* @example
* ```ts
* const progress = useAnimatedValue(0);
* ```
*/
export function useAnimatedValue(
initialValue: number,
config?: Animated.AnimatedConfig,
): Animated.Value {
const ref = useRef<Animated.Value | null>(null);
if (ref.current === null) {
ref.current = new Animated.Value(initialValue, config);
}
return ref.current;
}