阻止返回
有时你可能想要阻止用户离开某个屏幕,例如,如果存在未保存的更改,你可能想要显示一个确认对话框。你可以使用 beforeRemove
事件来实现这一点。
事件监听器接收触发它的 action
。你可以在确认后再次调度此操作,或检查操作对象以确定要执行的操作。
示例
function EditText({ navigation }) {
const [text, setText] = React.useState('');
const hasUnsavedChanges = Boolean(text);
React.useEffect(
() =>
navigation.addListener('beforeRemove', (e) => {
if (!hasUnsavedChanges) {
// If we don't have unsaved changes, then we don't need to do anything
return;
}
// Prevent default behavior of leaving the screen
e.preventDefault();
// Prompt the user before leaving the screen
Alert.alert(
'Discard changes?',
'You have unsaved changes. Are you sure to discard them and leave the screen?',
[
{ text: "Don't leave", style: 'cancel', onPress: () => {} },
{
text: 'Discard',
style: 'destructive',
// If the user confirmed, then we dispatch the action we blocked earlier
// This will continue the action that had triggered the removal of the screen
onPress: () => navigation.dispatch(e.data.action),
},
]
);
}),
[navigation, hasUnsavedChanges]
);
return (
<TextInput
value={text}
placeholder="Type something…"
onChangeText={setText}
/>
);
}
以前,实现此功能的方法是
- 覆盖标题中的返回按钮
- 禁用向后滑动手势
- 覆盖 Android 上的系统返回按钮/手势
但是,除了代码更少之外,这种方法还有许多重要的区别
- 它不与任何特定按钮耦合,从自定义按钮返回也会触发它
- 它不与任何特定操作耦合,任何从状态中移除路由的操作都会触发它
- 它适用于嵌套导航器,例如,如果屏幕由于父导航器中的操作而被移除
- 用户仍然可以在堆栈导航器中向后滑动,但是,如果事件被阻止,滑动将被取消
- 可以继续执行触发事件的相同操作
限制
使用 beforeRemove
事件时,需要注意一些限制。该事件**仅**在由于导航状态更改而移除屏幕时触发。例如
- 用户在堆栈中的屏幕上按下了返回按钮。
- 用户执行了向后滑动手势。
- 一些操作,例如
pop
或reset
被调度,这会从状态中移除屏幕。
当屏幕被取消焦点但未被移除时,此事件**不会**被触发。例如
- 用户在堆栈中将新屏幕推送到带有侦听器的屏幕之上。
- 用户从一个标签/抽屉屏幕导航到另一个标签/抽屉屏幕。
当用户由于导航状态无法控制的操作而退出屏幕时,该事件也不会被触发
- 用户关闭应用程序(例如,通过按下主屏幕上的返回按钮,关闭浏览器中的标签页,从应用程序切换器中关闭应用程序等)。您还可以使用
hardwareBackPress
事件在 Android 上,beforeunload
事件在 Web 上等来处理其中的一些情况。 - 屏幕由于条件渲染或父组件被卸载而被卸载。
- 屏幕由于使用
@react-navigation/bottom-tabs
、@react-navigation/drawer
等的unmountOnBlur
选项而被卸载。
除了上述情况之外,此功能也不适用于 @react-navigation/native-stack
。要使其正常工作,您需要
- 禁用屏幕的滑动手势 (
gestureEnabled: false
)。 - 使用自定义返回按钮覆盖标题中的原生返回按钮 (
headerLeft: (props) => <CustomBackButton {...props} />
)。