React Native - Device back button handling

后端 未结 9 2141
感情败类
感情败类 2020-12-02 18:33

I want to check if there are more than one screens are on stack when device back button is hit. If yes, I want to show previous screen and if no, I want to exit app.

9条回答
  •  刺人心
    刺人心 (楼主)
    2020-12-02 19:00

    I am on v0.46.0 of react-native and had the same issue. I tracked the issue down to this file in the react-native code base

    https://github.com/facebook/react-native/blob/master/Libraries/Utilities/BackHandler.android.js#L25

    When running with the chrome debugger turned off the line

    var subscriptions = Array.from(_backPressSubscriptions.values()).reverse()
    

    always returns an empty array for subscriptions which in turn causes the invokeDefault variable to stay true and the .exitApp() function to be called.

    After more investigation, I think the issue was discovered and discussed in the following PR #15182.

    Even after copy/pasting the PR change in an older version of RN it did not work most likely caused by the issue described in the PR.

    After some very slight modifications I got it working by changing to

    RCTDeviceEventEmitter.addListener(DEVICE_BACK_EVENT, function() {
      var invokeDefault = true;
      var subscriptions = []
      _backPressSubscriptions.forEach(sub => subscriptions.push(sub))
    
      for (var i = 0; i < subscriptions.reverse().length; ++i) {
        if (subscriptions[i]()) {
          invokeDefault = false;
          break;
        }
      }
    
      if (invokeDefault) {
        BackHandler.exitApp();
      }
    });
    

    Simply using a .forEach which was the original implementation on the PR before the amended Array.from syntax works throughout.

    So you could fork react-native and use a modified version, submit a PR though I imagine that will take a little while to be approved and merged upstream, or you can do something similar to what I did which was to override the RCTDeviceEventEmitter.addListener(...) for the hardwareBackPress event.

    // other imports
    import { BackHandler, DeviceEventEmitter } from 'react-native'
    
    class MyApp extends Component {
      constructor(props) {
        super(props)
        this.backPressSubscriptions = new Set()
      }
    
      componentDidMount = () => {
        DeviceEventEmitter.removeAllListeners('hardwareBackPress')
        DeviceEventEmitter.addListener('hardwareBackPress', () => {
          let invokeDefault = true
          const subscriptions = []
    
          this.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()
          }
        })
    
        this.backPressSubscriptions.add(this.handleHardwareBack)
      }
    
      componentWillUnmount = () => {
        DeviceEventEmitter.removeAllListeners('hardwareBackPress')
        this.backPressSubscriptions.clear()
      }
    
      handleHardwareBack = () => { /* do your thing */ }
    
      render() { return  }
    }
    

提交回复
热议问题