ロゴテキスト ロゴ

    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
    都内でフリーランスのエンジニアをやってます。フロントとアプリ開発メインに幅広くやってます。