Reactでinput要素外でKeyDownの処理を行う(TS版)

投稿日
Reactでinput要素外でKeyDownの処理を行う(TS版)

以前、inputやtextarea要素でキーボードの特定キーを入力された時の処理について記載しました。




inputやtextareaが選択されている状態の場合onKeyDownというメソッドを用いて処理が行えます。


しかし「何も選択されていない状態の時に特定のキーを押された事を契機として処理を行う

というケースの場合onKeyDownは使用出来ないため、そのような状況の対応方法を記載します。

検証した環境

1 react 17.0.2
2 @types/react 17.0.8

特定のキーを押下時に処理を行う

何も選択されていない状態で、特定のキーを押下時に処理を行うにはaddEventListenerを使います


addEventListenerを使ってkeydownを検知しメソッドを呼び出し、

どのキーが押されたかに応じて判定をします。


下記の場合は「Escape」キーを押された場合

import React, { useEffect, VFC } from 'react'
 
const Sample: VFC = () => {
  const handleKeyDown = (event: KeyboardEvent) => {
    // Escapeキーの場合処理を行う
    if (event.key === 'Escape') {
      console.log('keydown Escape Key')
    }
  }
 
  useEffect(() => {
    document.addEventListener('keydown', handleKeyDown, false)
  }, [])
 
  return <div>sample</div>
}
 
export default Sample

どのキーが押されたか判断するのは event.keyCodeが非推奨になっているので、

event.keyevent.codeを使うのが良いみたいです

keyCode が deprecated になってた


useStateと組み合わせて使う

もし useState の値に応じてhandleKeyDownの処理を変えたい場合注意が必要で、

addEventListenerを登録する useEffect のdepsに useState の値を記載する必要があります

import React, { useEffect, useState, VFC } from 'react'
 
const Sample: VFC = () => {
  const [open, setOpen] = useState(false)
 
  const handleKeyDown = (event: KeyboardEvent) => {
    if (event.key === 'Escape' && open) {
      console.log('keydown Escape Key')
      setOpen(false)
    }
  }
 
  useEffect(() => {
    document.addEventListener('keydown', handleKeyDown, false)
  }, [open]) //depsに useState の値が必要
 
  return <div>sample</div>
}
 
export default Sample

ただし、このように書くとNext.jsのLintで怒られます

15:6  Warning: React Hook useEffect has a missing dependency: 'handleKeyDown'. Either include it or remove the dependency array.
react-hooks/exhaustive-deps
Lintでメソッドをdepsに書いてほしい、というwarningが出る



じゃあhandleKeyDownをdepsに書けばいいかと書いてみると

6:9  Warning: The 'handleKeyDown' function makes the dependencies of useEffect Hook (at line 15) change on every render.
Move it inside the useEffect callback.
Alternatively, wrap the definition of 'handleKeyDown' in its own useCallback() Hook.
react-hooks/exhaustive-deps
LintでuseCallbackを使ってほしい、というwarningが出る

とまた怒られるので、


直していくと最終的に以下のようになります

import React, { useEffect, useCallback, useState, VFC } from 'react'
 
const Sample: VFC = () => {
  const [open, setOpen] = useState(false)
 
  const handleKeyDown = useCallback(
    (event: KeyboardEvent) => {
      if (event.key === 'Escape' && open) {
        console.log('keydown Escape Key')
        setOpen(false)
      }
    },
    [open]
  )
 
  useEffect(() => {
    document.addEventListener('keydown', handleKeyDown, false)
  }, [open, handleKeyDown]) //depsに useState と useCallbackでラップした関数 が必要
 
  return <div>sample</div>
}
 
export default Sample

handleKeyDownuseCallbackでラップし、depsで使っているuseStateの値を記載します

プロフィール画像
Yuki Takara
都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。