跳到主要內容
版本: 6.x

預防返回

有時您可能想要阻止使用者離開某個畫面,例如如果有未儲存的變更,您可能會想要顯示確認對話方塊。您可以使用 beforeRemove 事件來達成此目的。

事件偵聽器接收觸發它的 動作 。您可以在確認後再次發送此動作,或檢查動作物件以決定要執行什麼動作。

範例

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 事件時,有幾個限制事項須注意。這個事件在由於導航狀態變更而移除畫面時觸發。例如

  • 使用者在堆疊中的畫面中按下返回按鈕。
  • 使用者執行後刷手勢。
  • 派遣某些動作,例如 popreset,移除狀態中的畫面。

這個事件在畫面失去焦點但未移除時觸發。例如

  • 使用者在堆疊中的具有偵聽器的畫面上方推入新畫面。
  • 使用者從一個標籤/抽屜畫面導航到另一個標籤/抽屜畫面。

當使用者因導航狀態無法控制的動作而退出畫面時,這個事件也不會觸發

  • 使用者關閉應用程式(例如,按主畫面上的返回按鈕、關閉瀏覽器中的標籤、從應用程式切換器中關閉等)。您還可以在 Android 上使用 hardwareBackPress 事件、在網頁上使用 beforeunload 事件等來處理其中的一些案例。
  • 由於條件式渲染或由於父元件解除掛載而導致畫面解除掛載。
  • 由於使用 @react-navigation/bottom-tabs@react-navigation/drawer 等的 unmountOnBlur 選項而導致畫面解除掛載。

除了以上情況外,這個功能在 @react-navigation/native-stack 也無法正常運作。若要讓其運作,您需要

  • 停用畫面的後刷手勢 (gestureEnabled: false)。
  • 以自訂返回按鈕覆寫標頭中的原生返回按鈕 (headerLeft: (props) => <CustomBackButton {...props} />)。