デバイスの戻るボタンが押されたときに複数の画面がスタック上にあるかどうかを確認したい。はいの場合は前の画面を表示し、いいえの場合はアプリを終了します。
多くの例を確認しましたが、それらはBackAndroidとNavigatorを使用しています。ただし、どちらも非推奨です。 BackHandlerはBackAndroidの代替品です。また、props.navigation.goBack(null)を使用して前の画面を表示できます。
しかし、スタック内の画面カウントを見つけるためのコードを見つけることができません。非推奨のナビゲーターを使用したくない!
この例では、ほとんどのフローで一般的に予想される戻るナビゲーションを示します。予想される動作に応じて、すべての画面に次のコードを追加する必要があります。 2つのケースがあります。1.スタックに複数の画面がある場合、デバイスの戻るボタンは前の画面を表示します。 2.スタックに画面が1つしかない場合、デバイスの戻るボタンを押すとアプリが終了します。
ケース1:前の画面を表示する
import { BackHandler } from 'react-native';
constructor(props) {
super(props)
this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
}
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonClick);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButtonClick);
}
handleBackButtonClick() {
this.props.navigation.goBack(null);
return true;
}
重要:コンストラクタでメソッドをバインドし、componentWillUnmountでリスナーを削除することを忘れないでください。
ケース2:アプリを終了する
この場合、アプリを終了する画面上で何も処理する必要はありません。
重要:これはスタック上の画面のみである必要があります。
スタックに複数の画面がスタックされている場合、react-nativeのデフォルトの戻るボタンの動作は、スタック内の前の画面に戻ることです。アプリを終了する画面が1つしかない場合にデバイスの戻るボタンを押すと、カスタム設定が必要になります。ただし、特定のStackNavigatorのルーターのgetStateForActionメソッドを変更することで、画面ごとに処理コードを追加し直すことなく、これを実現できます。
アプリケーションで次のStackNavigatorが使用されているとします
const ScreenStack = StackNavigator(
{
'Screen1': {
screen: Screen1
},
'Screen2': {
screen: Screen2
},
},
{
initialRouteName: 'Screen1'
}
);
スタックナビゲーターのルーターのgetStateForActionメソッドを次のように変更して、予期されるバック動作を実現できます。
const defaultStackGetStateForAction =
ScreenStack.router.getStateForAction;
ScreenStack.router.getStateForAction = (action, state) => {
if(state.index === 0 && action.type === NavigationActions.BACK){
BackHandler.exitApp();
return null;
}
return defaultStackGetStateForAction(action, state);
};
state.index
は、スタックに1つの画面がある場合にのみ0
になります。
これを試してください反応ナビゲーション
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
handleBackButton = () => {
const pushAction = StackActions.Push({
routeName: 'DefaultSelections',
});
this.props.navigation.dispatch(pushAction);
}
現在の画面は "DefaultSelections"で、戻るボタンを押すと、同じボタンに移動し、戻るボタンを無効にすると、戻るボタンが無効になります。
return true
backButtonの場合(公式ドキュメントで提案されているとおり)disablesすべての戻るボタンscreens;不要
Guyzは、react nativeの問題だけではないことを理解してください。 firebaseと統合する際は注意してください。最近のFirebaseバージョンには、反応するネイティブアプリに戻るボタンを統合する問題があります!! firebaseバージョンをfirebase-version @ 5.0.3にダウングレードし、それが機能するかどうかを再確認してください!私は同じ問題を抱えていて、何日も心配していました。私はついに@ 5.0.3バージョンにダウングレードしましたが、今では戻るボタンは完璧に機能します!問題が引き続き発生する場合は、下位バージョンにダウングレードすることができます。
私はreact-nativeのv0.46.0を使用していますが、同じ問題がありました。反応ネイティブコードベースでこのファイルまで問題を追跡しました
https://github.com/facebook/react-native/blob/master/Libraries/Utilities/BackHandler.Android.js#L25
chromeデバッガーを使用して実行すると、ラインがオフになりました
var subscriptions = Array.from(_backPressSubscriptions.values()).reverse()
サブスクリプションに対して常に空の配列を返します。これにより、invokeDefault変数がtrueのままになり、.exitApp()関数が呼び出されます。
さらに調査した結果、この問題は次のように発見され、議論されたと思います PR#15182 。
古いバージョンのRNでPRの変更をコピー/貼り付けた後でも、PRに記載されている問題が原因で動作することはほとんどありませんでした。
いくつかの非常にわずかな変更を加えた後、
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();
}
});
修正されたArray.from構文が機能する前に、PRの元の実装であった.forEachを使用するだけです。
したがって、react-nativeをフォークして修正バージョンを使用し、PRを送信することができますが、承認されて上流にマージされるまで少し時間がかかると思います(...)hardwareBackPressイベント用。
// 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 <YourApp /> }
}
constructor(props){
super(props)
this.onBackPress = this.onBackPress.bind(this);
}
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', this.onBackPress);
}
componentWillUnmount(){
BackHandler.removeEventListener('hardwareBackPress', this.onBackPress);
}
onBackPress(){
const {dispatch, nav} = this.props;
if (nav.index < 0) {
return false;
}
dispatch(NavigationActions.back());
return true;
}
render(){
const {dispatch, nav} = this.props;
return(
<DrawerRouter
navigation= {
addNavigationHelpers({
dispatch,
state: nav,
addListener,
})
}
/>
);
}