モジュールごとにリスナを登録していくと 後から強制的に移動させたい場合に解除が面倒
なので ページ遷移を防ぐ処理だけをモジュールにまとめた
let listener_added = false
const set = new Set()
const listener = (event) => event.returnValue = "preventer"
export const prevent = (key) => {
set.add(key)
if (set.size && !listener_added) {
window.addEventListener("beforeunload", listener)
listener_added = true
}
}
export const unprevent = (key) => {
set.delete(key)
if (!set.size && listener_added) {
window.removeEventListener("beforeunload", listener)
listener_added = false
}
}
export const forceUnpreventAll = () => {
for (const key of set) {
unprevent(key)
}
}
export const getReasons = () => [...set]
prevent 関数の引数にキーを入れて呼び出す
キーは任意の型で文字列とかシンボルとか
1 つ以上登録されてればページ遷移しようとするとダイアログが出る
unprevent 関数の引数にキーを入れて呼び出すと 対応する prevent を解除する
forceUnpreventAll 関数を呼び出すと全部の prevent を解除する
ただし このモジュール外で登録した beforeunload があれば ダイアログは出る
getReasons 関数でキーのリストを取得できる
現状がページ遷移がブロックされる状態かの判断や 何が原因でブロックされる状態なのかを確認できる
現状の実装だと prevent に登録されたキー数が 0, 1 の切り替わりでリスナの付け外しをしてるけど 常に付けておいて returnValue に代入するかを切り替えるでもよかったかも