11
6

More than 3 years have passed since last update.

React + react-use: useKeyフックでキーボードイベントを扱う

Last updated at Posted at 2021-01-29

react-useは、便利なフックを詰め合わせたライブラリです。本稿はその中から、キーボードイベントが簡単に扱えるuseKeyをご紹介します。作例はCodeSandboxに公開しました。

Create React Appでカウンターアプリケーションをつくる

サンプルにするのは、よくとり上げられるカウンターです。React公式サイトの「ステートフックの利用法」でも、useStateフックの説明に使われています。

Reactアプリケーションのひな型は、モジュール分けできるCreate Reac Appでつくることにしましょう(「Create React App 入門 01: 3×3マスのゲーム盤をつくる」01「Reactアプリケーションのひな形をつくる」参照)。

npx create-react-app react-usekey

ルートコンポーネントのモジュールsrc/App.jsは、つぎのコード001のように書き改めます。useStateでカウンター数値の状態変数(count)をひとつ定め、減算(decrement)と加算(increment)のハンドラを加えました。CounterDisplayは、このあと新たにつくるカウンター表示のコンポーネントです。

コード001■useStateとプロパティを用いたルートモジュール

src/App.js
import React, { useState } from 'react';
import CounterDisplay from './CounterDisplay';

const initialCount = 0;
function App() {
    const [count, setCount] = useState(initialCount);
    const decrement = () => setCount((count) => --count);
    const increment = () => setCount((count) => ++count);
    return (
        <div className="App">
            <CounterDisplay counter={{ count, decrement, increment }} />
        </div>
    );
}

export default App;

カウンターを表示するコンポーネントのモジュールsrc/CounterDisplay.jsは、つぎのコード002のように定めました。コンポーネントが受け取ったプロパティ(counter)から、カウンター数値(count)と減算(decrement)・加算(increment)のハンドラを取り出して、JSXのテキストとボタンに与えましょう。

コード002■カウンター表示のモジュール

src/CounterDisplay.js
import React from "react";

const CounterDisplay = ({ counter }) => {
    return (
        <div>
            <button onClick={counter.decrement}>-</button>
            <span>{counter.count}</span>
            <button onClick={counter.increment}>+</button>
        </div>
    );
}
export default CounterDisplay;

これで、数値をボタンで増減できるカウンターのでき上がりです(図001)。まだ、キーボードイベントには対応していません。

図001■でき上がったカウンター

2009001_001.png

react-useのuseKeyフックを使う

では、いよいよuseKeyフックを使います。まずは、アプリケーションへのreact-useのインストールです。

npm install react-use

そうしたら、キーボードイベントを扱いたいコンポーネントに、useKeyimportしてください。上下の矢印キーで、カウンターを増減したいと思います。

useKeyの使い方は、つぎのように気が抜けるほど簡単です。第1引数にキーを示すKeyboardEvent.keyプロパティの値、第2引数にイベントハンドラを渡します。JSXのどの要素にイベントハンドラを加えるか、などと悩まなくて済むのです。

src/App.js
import { useKey } from 'react-use';

function App() {

    useKey('ArrowDown', decrement);
    useKey('ArrowUp', increment);

}

ここまでのルートモジュールsrc/App.jsの記述全体を、つぎのコード003にまとめました。

コード003■上下矢印キーでカウンターを増減させるルートモジュール

src/App.js
import React, { useState } from 'react';
import {useKey} from 'react-use';
import CounterDisplay from './CounterDisplay';

const initialCount = 0;
function App() {
    const [count, setCount] = useState(initialCount);
    const decrement = () => setCount((count) => --count);
    const increment = () => setCount((count) => ++count);
    useKey('ArrowDown', decrement);
    useKey('ArrowUp', increment);
    return (
        <div className="App">
            <CounterDisplay counter={{ count, decrement, increment }} />
        </div>
    );
}

export default App;

[control]/[Ctrl] + [esc]キーでカウンターをリセットする

キーボードイベントの処理をもうひとつ加えます。[control]/[Ctrl] + [esc]キーを押したら、カウンターの数値を0にリセットしましょう。[esc]キーのKeyboardEvent.keyプロパティの値はEscapeです。[control]/[Ctrl]キーを押しているかどうかは、KeyboardEvent.ctrlKeyで調べられます。

useKeyのつぎの構文で、第1引数はキー判定する関数です。キーイベントを受け取るので、ハンドラを呼び出すキーかどうか論理値で返します。第3引数のeventプロパティに定めるのは、keydown/keypress/keyupのいずれかのキーボードイベントです。

const キー判定関数 = (event) => 判定した論理値;
useKey(キー判定関数, イベントハンドラ, {event: イベント});

キー判定関数では、つぎのように[control]/[Ctrl] + [esc]キーをイベント(event)のプロパティから調べます。キーボードイベントはkeyupとしました。ルートモジュールの記述は、以下のコード004にまとめたとおりです。作例はCodeSandboxに公開しましたので、動きやモジュールごとのコードはこちらでお確かめください。

src/App.js
function App() {

    const predicate = (event) => event.ctrlKey && event.key === 'Escape';
    const escKeyUpHandler = () => setCount(0);
    useKey(predicate, escKeyUpHandler, {event: 'keyup'});

}

コード004■[control]/[Ctrl] + [esc]キーでカウンターをリセットするルートモジュール

src/App.js
import React, { useState } from 'react';
import {useKey} from 'react-use';
import CounterDisplay from './CounterDisplay';

const initialCount = 0;
function App() {
    const [count, setCount] = useState(initialCount);
    const decrement = () => setCount((count) => --count);
    const increment = () => setCount((count) => ++count);
    useKey('ArrowDown', decrement);
    useKey('ArrowUp', increment);
    const predicate = (event) => event.ctrlKey && event.key === 'Escape';
    const escKeyUpHandler = () => setCount(0);
    useKey(predicate, escKeyUpHandler, {event: 'keyup'});
    return (
        <div className="App">
            <CounterDisplay counter={{ count, decrement, increment }} />
        </div>
    );
}

export default App;

オブジェクトの分割代入を使う

ここで、オブジェクトの分割代入の構文をご紹介しましょう。前掲コード004のキー判定関数(predicate())で、引数のキーボードイベントからプロパティを取り出すコードです。オブジェクトの分割代入を使えば、つぎのように引数からプロパティが簡単に取り出せます。

// const predicate = (event) => event.ctrlKey && event.key === "Escape";
const predicate = ({ ctrlKey, key }) => ctrlKey && key === "Escape";

[追記: 2021年01月30日]「オブジェクトの分割代入を使う」の項を追加。

11
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
6