usePreventRemove
usePreventRemove
Hook 允许你阻止用户离开屏幕。例如,如果有未保存的更改,你可能希望在用户可以导航离开之前显示确认对话框。
该 Hook 接受 2 个参数
preventRemove
:一个布尔值,指示是否阻止屏幕被移除。callback
:一个函数,当移除被阻止时将被调用。这可以用于显示确认对话框。
回调函数接收一个 data
对象,其中包含触发屏幕移除的 action
。你可以在确认后再次 dispatch 这个 action,或者检查 action 对象来确定要执行的操作。
示例
- 静态
- 动态
const EditTextScreen = () => {
const [text, setText] = React.useState('');
const navigation = useNavigation();
const hasUnsavedChanges = Boolean(text);
usePreventRemove(hasUnsavedChanges, ({ data }) => {
if (Platform.OS === 'web') {
const discard = confirm(
'You have unsaved changes. Discard them and leave the screen?'
);
if (discard) {
navigation.dispatch(data.action);
}
} else {
Alert.alert(
'Discard changes?',
'You have unsaved changes. Discard them and leave the screen?',
[
{ text: "Don't leave", style: 'cancel', onPress: () => {} },
{
text: 'Discard',
style: 'destructive',
onPress: () => navigation.dispatch(data.action),
},
]
);
}
});
return (
<View style={styles.content}>
<TextInput
autoFocus
style={styles.input}
value={text}
placeholder="Type something…"
onChangeText={setText}
/>
</View>
);
};
const EditTextScreen = () => {
const navigation = useNavigation();
const [text, setText] = React.useState('');
const hasUnsavedChanges = Boolean(text);
usePreventRemove(hasUnsavedChanges, ({ data }) => {
if (Platform.OS === 'web') {
// Alert is not supported on web, so we can use confirm
const discard = confirm(
'You have unsaved changes. Discard them and leave the screen?'
);
if (discard) {
navigation.dispatch(data.action);
}
} else {
// Prompt the user before leaving the screen
Alert.alert(
'Discard changes?',
'You have unsaved changes. Discard them and leave the screen?',
[
{
text: "Don't leave",
style: 'cancel',
onPress: () => {
// Do nothingP
},
},
{
text: 'Discard',
style: 'destructive',
onPress: () => navigation.dispatch(data.action),
},
]
);
}
});
return (
<View style={styles.content}>
<TextInput
autoFocus
style={styles.input}
value={text}
placeholder="Type something…"
onChangeText={setText}
/>
</View>
);
};
在内部,该 Hook 使用 beforeRemove
事件来阻止屏幕被移除。每当由于导航操作而移除屏幕时,都会触发此事件。
局限性
使用 usePreventRemove
Hook 时,需要注意一些局限性。它仅在由于导航状态更改而移除屏幕时触发。例如
- 用户按下了堆栈中屏幕的返回按钮。
- 用户执行了滑动返回手势。
- 某些 action(例如
pop
或reset
)被 dispatch,从而从状态中移除了屏幕。
如果屏幕未被移除,它不会阻止屏幕失去焦点。例如
- 用户在堆栈中将新屏幕推到带有监听器的屏幕之上。
- 用户从一个标签页/抽屉屏幕导航到另一个标签页/抽屉屏幕。
当用户由于不受导航状态控制的操作而退出屏幕时,它也不会阻止屏幕被移除
- 用户关闭应用程序(例如,通过按下主屏幕上的返回按钮、关闭浏览器中的标签页、从应用切换器中关闭等)。你可以额外使用 Android 上的
hardwareBackPress
事件、Web 上的beforeunload
事件等来处理其中一些情况。有关更多详细信息,请参阅 阻止用户离开应用。 - 由于条件渲染或父组件被卸载,屏幕被卸载。
用户体验考虑
总的来说,我们建议谨慎使用此 Hook。更好的方法是将未保存的数据持久化到 AsyncStorage
或类似的持久存储中,并在用户返回屏幕时提示恢复它。
这样做有几个好处
- 如果应用程序关闭或意外崩溃,此方法仍然有效。
- 这对用户的干扰较小,因为他们仍然可以导航离开屏幕去检查某些内容,并在返回时不会丢失数据。