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日]「オブジェクトの分割代入を使う」の項を追加。