我想检查设备后退按钮被击中时是否有多个屏幕在堆栈中。如果是,我想显示前一个屏幕,如果不是,我想退出应用程序。
我已经检查了一些示例,但是那些使用了BackAndroid和Navigator。但他们俩都被弃用了。 BackHandler是BackAndroid的替代品。我可以使用props.navigation.goBack(null)显示上一个屏幕。
但是我找不到用于在堆栈中查找屏幕数的代码。我不想使用已弃用的Navigator!
答案 0 :(得分:31)
此示例将向您显示通常在大多数流程中预期的返回导航。您必须根据预期的行为向每个屏幕添加以下代码。有2种情况: 1.如果堆叠上有多个屏幕,设备后退按钮将显示上一个屏幕。 2.如果堆叠中只有一个屏幕,设备后退按钮将退出应用程序。
案例1:显示上一个屏幕
{{1}}
重要提示:不要忘记在构造函数中绑定方法并删除componentWillUnmount中的侦听器。
案例2:退出应用程序
在这种情况下,无需在该屏幕上处理要退出应用程序的任何内容。
重要事项:这应该只是堆叠屏幕。
答案 1 :(得分:12)
在反应钩子中
import { BackHandler } from 'react-native';
function handleBackButtonClick() {
navigation.goBack();
return true;
}
useEffect(() => {
BackHandler.addEventListener('hardwareBackPress', handleBackButtonClick);
return () => {
BackHandler.removeEventListener('hardwareBackPress', handleBackButtonClick);
};
}, []);
答案 2 :(得分:8)
import { BackHandler } from 'react-native';
constructor() {
super();
this.handleBackButtonClick = this.handleBackButtonClick.bind(this);
}
componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButtonClick);
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', this.handleBackButtonClick);
}
handleBackButtonClick() {
//this.props.navigation.goBack(null);
BackHandler.exitApp();
return true;
}
答案 3 :(得分:3)
如果堆栈中堆叠了多个屏幕,则react-native中的默认后退按钮行为将导航回堆栈中的上一个屏幕。当只有一个屏幕退出应用程序时,按下设备后退按钮需要自定义设置。然而,这可以通过修改特定StackNavigator路由器的getStateForAction方法而不必向每个屏幕添加处理代码来实现。
假设您在应用程序中使用了以下StackNavigator
Transform
可以按如下方式修改堆栈导航器路由器的getStateForAction方法,以实现预期的反向行为。
const ScreenStack = StackNavigator(
{
'Screen1': {
screen: Screen1
},
'Screen2': {
screen: Screen2
},
},
{
initialRouteName: 'Screen1'
}
);
只有当堆叠中有一个屏幕时,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
。
答案 4 :(得分:2)
Guyz请务必理解,这可能不仅是react native问题。将其与Firebase集成时请小心。 最新的firebase版本存在在本机应用程序中集成后退按钮的问题!! 请将Firebase版本降级为firebase-version @ 5.0.3,然后重新检查其是否有效! 我有同样的问题,担心了好几天。我终于降级到@ 5.0.3版本,现在后退按钮可以正常使用! 如果仍然遇到问题,您可以降级到较低的版本。
答案 5 :(得分:1)
我正在使用react-native v0.46.0,并且遇到了同样的问题。我在react-native代码库中将此问题跟踪到了该文件
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();
}
});
仅使用.forEach,它是经过修订的Array.PR语法在PR上的原始实现。
因此您可以派生react-native并使用修改后的版本,提交PR,尽管我认为这将需要一些时间才能批准并合并到上游,或者您可以执行与我所做的类似的操作来覆盖RCTDeviceEventEmitter.addListener(...)用于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 /> }
}
答案 6 :(得分:1)
尝试一下 反应导航
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
}
handleBackButton = () => {
const pushAction = StackActions.push({
routeName: 'DefaultSelections',
});
this.props.navigation.dispatch(pushAction);
}
当前屏幕为“ DefaultSelections”,按下后退按钮时将切换到同一屏幕,因此后退按钮已禁用,如通过禁用后退按钮
return true
用于backButton(由官方文档建议)在所有屏幕上禁用后退按钮;不想
答案 7 :(得分:0)
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,
})
}
/>
);
}
答案 8 :(得分:0)
这是我使用某些条件成功实施的方法:
componentWillMount() {
BackHandler.addEventListener(
'hardwareBackPress',
this.handleBackButtonClick,
);
}
componentWillUnmount() {
BackHandler.removeEventListener(
'hardwareBackPress',
this.handleBackButtonClick,
);
}
handleBackButtonClick = () => {
//some condition
if (this.state.isSearchBarActive) {
this.setState({
isSearchBarActive: false,
});
this.props.navigation.goBack(null);
return true;
}
return false;
};
答案 9 :(得分:0)
实用函数可能非常有用:
backPressHandler.js
import React from 'react';
import {BackHandler} from 'react-native';
const onBackPress = (callback) => {
BackHandler.addEventListener('hardwareBackPress', callback);
return () => {
BackHandler.removeEventListener('hardwareBackPress', callback);
};
};
export {onBackPress};
现在在我的屏幕上:
myScreen.js
import {onBackPress} from '../utils/backPressHandler';
function handleBackPress() {
navigation.goBack();
return true;
}
useEffect(() => {
onBackPress(handleBackPress);
}, []);
答案 10 :(得分:-1)
我用助焊剂进行导航。
const RouterComp = () => {
let backLoginScene=false;
return (
<Router
backAndroidHandler={() => {
const back_button_prohibited = ['login','userInfo','dashboard'];
if (back_button_prohibited.includes(Actions.currentScene) ) {
if (backLoginScene == false) {
ToastAndroid.show("Click back again to exit.", ToastAndroid.SHORT);
backLoginScene = !backLoginScene;
setTimeout(() => {
backLoginScene = false;
}, 2000);
return true;
} else {
backLoginScene = false;
BackHandler.exitApp();
}
return false;
}}}>
<Scene key='root' hideNavBar>
<Scene key='guest' hideNavBar >
<Scene key='login' component={Login} ></Scene>
<Scene key='userInfo' component={UserInfo}></Scene>
</Scene>
<Scene key='user' hideNavBar>
<Scene key='dashboard' component={Dashboard} title='Dashboard' initial />
<Scene key='newAd' component={NewAd} title='New Ad' />
</Scene>
</Scene>
</Router>
)
}
export default RouterComp;