React Router 的 useBeforeUnload
离开前
为了弹出类似上述的确认对话框,我按以下方式进行了注册。
window.addEventListener('beforeunload', (e) => {
e.preventDefault();
e.returnValue = '';
})
在Chrome浏览器中,不仅需要调用preventDefault,还需要将字符串填充到事件的returnValue中。
在其他浏览器中,除了调用preventDefault之外,通常还需要将字符串填充到事件的returnValue中,或将字符串作为事件处理程序的返回值传递,以避免确认对话框的显示。
使用BeforeUnload
在React中,当需要与React之外的系统进行连接时,需要通过使用effect来实现。window.addEventListener也是其中的一个例子,通常的实现如下。
useEffect(() => {
window.addEventListener(eventName, handler);
return () => {
window.removeEventListener(eventName, handler);
}
}, []);
React Router提供了useBeforeUnloadAPI作为在注册事件处理程序时简化beforeunload实现的助手。如果您希望在卸载页面时显示确认对话框,可以按照以下方式进行描述。
useBeforeUnload(
useCallback((e: BeforeUnloadEvent) => {
e.preventDefault();
e.returnValue = '';
}, [state]),
{ capture: true }
);
第1个参数是必需的,用于注册事件处理程序。此处理程序必须用useCallback进行包装。
第2个参数是可选的,用于指定行为选项。选项是一个以capture为键的对象。capture是一个布尔值,如果为true,则在捕获阶段触发事件,如果为false,则在冒泡阶段触发。
默认为false,因此如果要将其设置为true,则需要指定第2个参数的用法。
// captureがtrueの場合
useBeforeUnload(
useCallback((e: BeforeUnloadEvent) => {
console.log(1);
}, [state]),
{ capture: true }
);
// captureがfalse(デフォルト)の場合
useBeforeUnload(
useCallback((e: BeforeUnloadEvent) => {
console.log(2);
}, [state])
);
实施如下。
export function useBeforeUnload(
callback: (event: BeforeUnloadEvent) => any,
options?: { capture?: boolean }
): void {
let { capture } = options || {};
React.useEffect(() => {
let opts = capture != null ? { capture } : undefined;
window.addEventListener("beforeunload", callback, opts);
return () => {
window.removeEventListener("beforeunload", callback, opts);
};
}, [callback, capture]);
}
这个代码使用了常见的以效果(effect)为基础的写法。我们可以看到,之所以需要用useCallback将事件处理程序包裹起来,是因为它在效果(effect)内部使用了。
通过将其作为API来划分,可以隐藏在注册事件处理器时可能引起的复杂性,所以我认为在不使用React Router的情况下,可以定义类似的API,将事件处理器的注册抽象成通用化的API来实现。
export function useEventListener<K extends keyof WindowEventMap>(
eventName: T,
callback: (event: WindowEventMap[T]) => void,
options?: boolean | AddEventListenerOptions,
): void {
useEffect(() => {
window.addEventListener(eventName, callback, options);
return () => {
windowremoveEventListener(eventName, callback, options);
};
}, [eventName, handler, options]);
}