React Router 的 useBeforeUnload

离开前

スクリーンショット 2023-11-02 11.20.22.png

为了弹出类似上述的确认对话框,我按以下方式进行了注册。

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]);
}
广告
将在 10 秒后关闭
bannerAds