戻る動作の禁止
保存されていない変更がある場合など、ユーザーが画面を離れるのを防ぎたい場合があります。確認ダイアログを表示したい場合などです。これは、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
などのアクションがディスパッチされました。
このイベントは、画面が非フォーカスになっているが削除されていない場合はトリガーされません。例えば
- ユーザーがスタック内のリスナーのある画面の上に新しい画面をプッシュしました。
- ユーザーがタブ/ドロワー画面間を移動しました。
また、ナビゲーション状態によって制御されないアクションによってユーザーが画面を終了する場合にも、このイベントはトリガーされません。
- ユーザーがアプリを閉じます(例:ホーム画面で戻るボタンを押す、ブラウザでタブを閉じる、アプリスイッチャーから閉じるなど)。さらに、Androidでは
hardwareBackPress
イベント、Webではbeforeunload
イベントなどを追加で使用して、これらのケースの一部を処理できます。 - 条件付きレンダリング、または親コンポーネントがアンマウントされたために画面がアンマウントされます。
@react-navigation/bottom-tabs
、@react-navigation/drawer
などでunmountOnBlur
オプションを使用することで、画面がアンマウントされます。
上記のシナリオに加えて、この機能は@react-navigation/native-stack
では正常に動作しません。動作させるには、
- 画面のスワイプジェスチャーを無効にします(
gestureEnabled: false
)。 - ヘッダーでネイティブの戻るボタンをカスタム戻るボタンでオーバーライドします(
headerLeft: (props) => <CustomBackButton {...props} />
)。