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とプロパティを用いたルートモジュール
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■カウンター表示のモジュール
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■でき上がったカウンター
react-useのuseKeyフックを使う
では、いよいよuseKeyフックを使います。まずは、アプリケーションへのreact-useのインストールです。
npm install react-use
そうしたら、キーボードイベントを扱いたいコンポーネントに、useKeyをimportしてください。上下の矢印キーで、カウンターを増減したいと思います。
useKeyの使い方は、つぎのように気が抜けるほど簡単です。第1引数にキーを示すKeyboardEvent.keyプロパティの値、第2引数にイベントハンドラを渡します。JSXのどの要素にイベントハンドラを加えるか、などと悩まなくて済むのです。
import { useKey } from 'react-use';
function App() {
useKey('ArrowDown', decrement);
useKey('ArrowUp', increment);
}
ここまでのルートモジュールsrc/App.jsの記述全体を、つぎのコード003にまとめました。
コード003■上下矢印キーでカウンターを増減させるルートモジュール
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に公開しましたので、動きやモジュールごとのコードはこちらでお確かめください。
function App() {
const predicate = (event) => event.ctrlKey && event.key === 'Escape';
const escKeyUpHandler = () => setCount(0);
useKey(predicate, escKeyUpHandler, {event: 'keyup'});
}
コード004■[control]/[Ctrl] + [esc]キーでカウンターをリセットするルートモジュール
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日]「オブジェクトの分割代入を使う」の項を追加。
