infinitered / ignite-bowser

Bowser is now re-integrated into Ignite CLI! Head to https://github.com/infinitered/ignite to check it out.
https://infinite.red/ignite
MIT License
615 stars 140 forks source link

Exit route not working on android back button press #357

Open archansel opened 4 years ago

archansel commented 4 years ago

What's going on?

After upgrading to react navigation 5, back button handle for exit route not working, in fact it's not even registering, so any routes defined in export const exitRoutes: string[] = ["welcome", "demo"] won't exit app, but trigger default bahavior


Expected result

App will exit if currently showing route in exitRoutes


Steps to reproduce

  1. Install fresh ignite bowser
  2. Change the exit routes, add "demo" route
  3. Run app
  4. Navigate to demo screen
  5. Press hardware back

It will go back to welcome screen instead of exiting app


Current workaround

  1. Using device event emiter, this will remove all hardwareBackPress handler from other libs too
    
    import { useEffect, useRef, useState } from "react"
    import { BackHandler, DeviceEventEmitter } from "react-native"
    import { NavigationContainerRef } from "@react-navigation/native"
    import getActiveRouteName from "./get-active-routename"

export function useBackButtonHandler( ref: React.RefObject, canExit: (routeName: string) => boolean, ) { const [backPressSubscriptions] = useState(new Set()) const canExitRef = useRef(canExit)

useEffect(() => { canExitRef.current = canExit }, [canExit])

useEffect(() => { // We'll fire this when the back button is pressed on Android. const handleBackPress = () => { const navigation = ref.current

  if (navigation == null) {
    return false
  }

  // grab the current route
  const routeName = getActiveRouteName(navigation.getRootState())

  // are we allowed to exit?
  if (canExitRef.current(routeName)) {
    // let the system know we've not handled this event
    return false
  }

  // we can't exit, so let's turn this into a back action
  if (navigation.canGoBack()) {
    navigation.goBack()
    return true
  }

  return false
}

// Subscribe when we come to life
DeviceEventEmitter.removeAllListeners("hardwareBackPress")
DeviceEventEmitter.addListener("hardwareBackPress", () => {
  let invokeDefault = true
  const subscriptions = []

  backPressSubscriptions.forEach(sub => subscriptions.push(sub))

  for (let i = 0; i < subscriptions.reverse().length; i += 1) {
    if (subscriptions[i]()) {
      invokeDefault = false
      break
    }
  }

  if (invokeDefault) {
    BackHandler.exitApp()
  }
})

backPressSubscriptions.add(handleBackPress)

// Unsubscribe when we're done
return () => {
  DeviceEventEmitter.removeAllListeners("hardwareBackPress")
  backPressSubscriptions.clear()
}

}, [ref]) }


2. Using custom back handler by react navigation 5 https://reactnavigation.org/docs/custom-android-back-button-handling, put this in every screen you want to exit on back press, current issue, it won't register on the first time, so back will still go back to previous screen

export function useExitOnBack(routeName: string) { useFocusEffect( useCallback(() => { const handleBackPress = () => { BackHandler.exitApp() return true }

  BackHandler.addEventListener("hardwareBackPress", handleBackPress)
  return () => BackHandler.removeEventListener("hardwareBackPress", handleBackPress)
}, [routeName]),

) }


---

`ignite doctor` results:

System platform darwin
arch x64
cpu 4 cores Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
directory BSQ Mobile /Users/Ansel/Desktop/Repositories/Projects/Radyalabs/BSQ Mobile

JavaScript node 12.17.0 /usr/local/bin/node npm 6.14.4 /usr/local/bin/npm
yarn 1.22.4 /usr/local/bin/yarn

Ignite ignite-cli 3.5.1 /usr/local/bin/ignite
ignite src build /Users/Ansel/.config/yarn/global/node_modules/ignite-cli/build navigation react-navigation
generators {"component":"ignite-bowser","model":"ignite-bowser","navigator":"ignite-bowser","screen":"ignite-bowser"}
createdWith 3.5.1
boilerplate ignite-bowser
boilerplateVersion 5.0.3

Android java 1.8.0_242 /usr/bin/java
android home - /Users/Ansel/Library/Android/sdk

iOS xcode 11.5
cocoapods 1.8.4 /usr/local/bin/pod

StijnCoolen commented 4 years ago

Seems like React navigation V5 does not support useEffect(), see: https://reactnavigation.org/docs/custom-android-back-button-handling/#why-not-use-component-lifecycle-methods

imfly commented 3 years ago

+1