BackHandler

BackHandler.addEventListener('hardwareBackPress', handler) lets JavaScript answer the system Back gesture or button. Handlers run last-registered first; return true to consume the event, false to pass it on until the default finishes the activity. BookNest's catalog closes the drawer first, then asks for a second press:

src/android/AndroidShell.tsx: Back closes the drawer, then needs a double press (excerpt)TSX
    const sub = BackHandler.addEventListener('hardwareBackPress', () => {
      if (open.current) {                          // 1. back closes the drawer first
        drawer.current?.closeDrawer();
        console.log('back: closed the drawer');
        return true;
      }
      if (Date.now() - lastBack.current > 2000) { // 2. first press on the catalog warns
        lastBack.current = Date.now();
        ToastAndroid.show('Press back again to leave BookNest', ToastAndroid.SHORT);
        console.log('back: toast shown');
        return true;
      }
      console.log('back: second press, leaving');  // 3. second press within 2 s exits
      return false;                                // let the system finish the activity
    });

The handler, registered once in a useEffect that returns sub.remove(), reads refs rather than state. adb shell input keyevent KEYCODE_BACK with the drawer open, then two presses half a second apart, logged:

Output of 124
 LOG  back: closed the drawer
 LOG  drawer closed
...
 LOG  back: toast shown
 LOG  back: second press, leaving

The cut lines are an earlier try with presses over 2 s apart. For apps targeting API 36, Android 16 turns on predictive back and stops calling onBackPressed(); React Native 0.86 36,878 's ReactActivity registers an OnBackPressedCallback so BackHandler still works, and BookNest's "predictiveBackGestureEnabled": false in app.json sets the temporary opt-out. While a Modal is open, use its onRequestClose instead.