💮

React HooksでWindowEventを扱う

1 min read

hooksの定義

hooksでwindow eventを扱うためにカスタムhookを定義します。

import { DependencyList, useEffect } from "react";

const useWindowEvent = <K extends keyof WindowEventMap>(
  type: K,
  listener: (this: Window, ev: WindowEventMap[K]) => any,
  deps: DependencyList,
  options?: boolean | AddEventListenerOptions
) =>
  useEffect(() => {
    if (window) {
      window.addEventListener(type, listener, options);
      return () => {
        window.removeEventListener(type, listener, options);
      };
    }
  }, deps);

基本的にはwindow.addEventListenerの引数と同じ方を使っているので汎用性の高いhookが使用できます。

使い方

cmd, ctrl + s でコンテンツを保存する時に"keydown"イベントを監視して関数を実行するなどのケースで使用できます。

const Index: FC = () => {
  ...
  const handleSave = useCallback(
    (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key === "s") {
        if (!isSubmitting) {
	  onSubmit(title, body);
	}
        e.preventDefault();
        return false;
      }
    },
    [title, body, isSubmitting])
   useWindowEvent("keydown", handleSave, [title, body, isSubmitting])
   ...
}

また上記を使用したカスタムhookを定義することもでき、
例としてbefore unloadイベント時にアラートを表示するカスタムhookを定義してみます。

export const useBeforeUnloadAlert = (
  diff: boolean | (() => boolean),
  deps: DependencyList
) => {
  const handler = useCallback(
    (e: BeforeUnloadEvent) => {
      if (typeof diff === "boolean" ? diff : diff()) {
        e.preventDefault();
        e.returnValue = "";
      }
    },
    [deps]
  );
  useWindowEvent("beforeunload", handler, deps);
};

上記を使用する事で以下のような形でhooksを利用する事ができました

const Index: FC = () => {
  ...
  useBeforeUnloadAlert(()=>{
    return props.title !== title;
  },[props.title, title])
// or
  useBeforeUnloadAlert(props.title !== title, [props.title, title])
  ...
}