以前、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.key
・event.code
を使うのが良いみたいです
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
じゃあ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
とまた怒られるので、
直していくと最終的に以下のようになります
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
handleKeyDown
をuseCallback
でラップし、depsで使っているuseState
の値を記載します