All files / packages/utils/src useBlockBackNavigation.ts

100% Statements 17/17
90% Branches 9/10
83.33% Functions 5/6
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 471x 1x           1x                   7x             7x         7x 7x 5x       7x 7x   1x   1x     1x      
import { useEffect, useLayoutEffect } from "react";
import { BackHandler, Platform } from "react-native";
 
import {
  NavigationProp,
  ParamListBase,
  usePreventRemove,
} from "@react-navigation/native";
 
type Options = {
  // When true, skips the gestureEnabled setOptions call. Use this when
  // gestureEnabled:false is already set statically in the navigator config so
  // the dynamic setOptions doesn't accidentally re-enable the gesture.
  skipGestureHandling?: boolean;
  navigation?: NavigationProp<ParamListBase>;
};
 
export const useBlockBackNavigation = (
  isSubmitting: boolean,
  { skipGestureHandling = false, navigation }: Options = {},
) => {
  // usePreventRemove is the correct way to block back navigation in native-stack.
  // The beforeRemove + e.preventDefault() approach is NOT supported in native-stack
  // on iOS — the screen gets removed natively before JS can cancel it.
  usePreventRemove(isSubmitting, () => {});
 
  // Disable the swipe gesture dynamically. Skipped when the navigator already
  // sets gestureEnabled:false statically — setOptions would otherwise override
  // the static option back to true when isSubmitting is false.
  useLayoutEffect(() => {
    if (skipGestureHandling || !navigation) return;
    navigation.setOptions({ gestureEnabled: !isSubmitting });
  }, [navigation, isSubmitting, skipGestureHandling]);
 
  // Block Android hardware back button
  useEffect(() => {
    if (Platform.OS !== "android" || !isSubmitting) return;
 
    const subscription = BackHandler.addEventListener(
      "hardwareBackPress",
      () => true,
    );
 
    return () => subscription.remove();
  }, [isSubmitting]);
};