ブラウザ自身またはタブを閉じる時に、大事な未保存情報がある場合、beforeunload
イベントハンドラーを登録することで、閉じる処理を止めて確認ダイアログを表示することが出来ます。
今回はreactでbeforeunload
ハンドラーを書く時にはまったことをメモします。
結論
確認ダイアログの表示・非表示と表示するメッセージのコントロールは
beforeunload
ハンドラーのevent.returnValue
だけでコントールすれば良い。beforeunload
ハンドラーでメッセージ文字列を返すような実装だと、変な動きがあるからです。
環境
- Win7+IE11
- MacOS 10.13.5 + Chrome 70.0
reactでの書き方
export default class MyComp extends React.Component {
constructor(props) {
super(props);
this.handleBeforeUnload = this.handleBeforeUnload.bind(this);
}
componentWillMount() {
// 登録
window.addEventListener('beforeunload', this.handleBeforeUnload);
}
componentWillUnmount() {
// 解除
window.removeEventListener('beforeunload', this.handleBeforeUnload);
}
handleBeforeUnload(e) {
if (this.props.hogeObject) {
// 仕様標準のメソッドを呼び出す
e.preventDefault();
// returnValueに画面に表示したいメッセージを設定(Chromeはそれを無視してブラウザ定義のメッセージを表示)
e.returnValue = '未保存のデータがありますが、本当に閉じますか?';
}
}
// ...
}
- 該当条件に当たった場合、e.returnValueにメッセージ文字列を設定すると、閉じる動作が停止されます。
-
e.preventDefault()
が仕様標準のお薦めなので、念のため書きます。
次のような書き方で困った!!!
ネットで調べるとよく出てくる記事には、マルチブラウザをサポートするために、e.returnValue
とハンドラーの戻り値を両方返すようなお薦め実装がありますが、実に試してみたら、下記のような変な動きがあることがわかりました。
- 最初は
hogeObject
がない状態で、確認ダイアログなしで、タブを閉じれます。 - 一旦
hogeObject
がある状態にして、タブを閉じると確認ダイアログが表示されます。(ここまでまた良い) - 他の画面に遷移してから、もう一回この画面を表示して、
hogeObject
がない
状態でタブを閉じると、確認ダイアログが表示
されてしまいます。 - デバッグしてみたら、
handleBeforeUnload
は初回と同じように空白文字列を返しています。 - 戻り値が空白でも、初回と違って確認ダイアログを表示するのが何故でしょうか?ブラウザがURL単位で覚えたのかな?
handleBeforeUnload(e) {
let msg = '';
if (this.props.hogeObject) {
// 仕様標準のメソッドを呼び出す
e.preventDefault();
// returnValueに画面に表示したいメッセージを設定(Chromeはそれを無視してブラウザ定義のメッセージを表示)
msg = '未保存のデータがありますが、本当に閉じますか?';
}
e.returnValue = msg;
return msg;
}
解決
ブラウザの裏仕様がわかりませんが、一旦標準に書いてあるe.returnValue
に戻り値を設定するように修正したら、今回の問題を解決しました。