こんにちは、かつコーチです。
前回は useState を使って、コンポーネントに状態を持たせる方法を解説しました。
状態を更新するには、ボタンを押す・文字を入力するといった「ユーザー操作」をきっかけにする必要があります。
今回は、そのきっかけを作る「イベントハンドリング」の書き方を見ていきましょう。
イベントハンドリングとは?
ユーザー操作に反応して処理を実行する仕組み
イベントハンドリングとは、クリックやキー入力などユーザーの操作(イベント)をきっかけに、特定の処理(ハンドラ関数)を実行する仕組みです。
Reactでは、JSXの要素に onClick や onChange といった属性を指定し、そこに実行したい関数を渡すことでイベントを扱います。
function App() {
const handleClick = () => {
console.log("クリックされました");
};
return <button onClick={handleClick}>押してね</button>;
}
素のHTMLのonclickとの違い
HTMLにも onclick="handleClick()" のような属性がありますが、Reactの onClick はいくつか異なる点があります。
まず、Reactでは onClick のようにキャメルケースで書きます(HTMLは全小文字の onclick)。
そして渡す値は文字列ではなく、{ } の中に関数そのものを渡します。
さらに、ブラウザ間の差異を吸収した「合成イベント(SyntheticEvent)」という独自のイベントオブジェクトを扱う点も特徴です。
基本の書き方
onClickの基本形
もっともシンプルな形は、あらかじめ定義した関数をそのまま渡すパターンです。
function LikeButton() {
const handleLike = () => {
alert("いいねしました!");
};
return <button onClick={handleLike}>いいね</button>;
}
handleLike という関数を定義し、onClick={handleLike} のように関数そのものを渡しているのがポイントです。
イベントハンドラに引数を渡す
ハンドラに追加の情報を渡したい場合は、アロー関数でラップします。
function TodoItem({ id, label }: { id: number; label: string }) {
const handleDelete = (targetId: number) => {
console.log(`ID: ${targetId} を削除しました`);
};
return (
<li>
{label}
<button onClick={() => handleDelete(id)}>削除</button>
</li>
);
}
onClick={() => handleDelete(id)} のように、アロー関数の中で handleDelete(id) を呼び出すことで、引数付きの関数呼び出しを実現できます。
イベントオブジェクトに型を付ける
イベントハンドラの引数として受け取れる「イベントオブジェクト」にも、TypeScriptで型を付けられます。
function ClickLogger() {
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log("クリック位置X座標:", e.clientX);
};
return <button onClick={handleClick}>座標を表示</button>;
}
React.MouseEvent<HTMLButtonElement> のように、イベントの種類と対象の要素の型を指定すると、e.clientX などのプロパティに補完が効くようになります。
よくあるつまずきポイント
関数を実行してしまう罠
私がReactを学び始めた頃に一番戸惑ったのが、この「関数の実行タイミング」のミスです。
❌ Before:onClickに関数呼び出しを書いてしまう
function Counter() {
const handleClick = () => {
console.log("クリックされました");
};
// ()を付けてしまうと、レンダリング時に即実行される
return <button onClick={handleClick()}>押してね</button>;
}
onClick={handleClick()} と () を付けてしまうと、ボタンを押したときではなく、コンポーネントがレンダリングされた瞬間に handleClick が実行されてしまいます。
しかもこの場合、handleClick() の戻り値(undefined)が onClick に渡されるので、ボタンを押しても何も起こらなくなります。
私は最初、ページを開いただけでコンソールにログが出力され、原因が分からずしばらく悩んだ記憶があります。
✅ After:関数そのものを渡す
function Counter() {
const handleClick = () => {
console.log("クリックされました");
};
// ()を付けず、関数の参照だけを渡す
return <button onClick={handleClick}>押してね</button>;
}
onClick={handleClick} と () を付けずに書くことで、「クリックされたときに実行してほしい関数」だけをReactに渡せます。
引数を渡したい場合は前述の通り onClick={() => handleClick(id)} のようにアロー関数で包むのが正しい書き方です。
「() を付けるとその場で実行され、付けないと参照が渡される」という違いは、JavaScriptの基本でもあるので、混乱したらぜひ思い出してください。
応用:一歩先の使い方
フォームのonChangeイベント
入力欄の値が変わるたびに処理をしたい場合は onChange を使います。
function SearchBox() {
const [keyword, setKeyword] = useState<string>("");
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setKeyword(e.target.value);
};
return (
<div>
<input value={keyword} onChange={handleChange} />
<p>検索キーワード:{keyword}</p>
</div>
);
}
e.target.value で入力欄の現在の値を取得し、useState の更新関数に渡すことで、入力内容とコンポーネントの状態を連動させています。
複数のイベントハンドラを整理する
1つのコンポーネントにイベントハンドラが増えてくると、どれが何のためのハンドラか分かりにくくなります。
function ProductCard() {
const handleAddToCart = () => console.log("カートに追加");
const handleAddToWishlist = () => console.log("お気に入りに追加");
const handleShare = () => console.log("シェア");
return (
<div>
<button onClick={handleAddToCart}>カートに追加</button>
<button onClick={handleAddToWishlist}>お気に入り</button>
<button onClick={handleShare}>シェア</button>
</div>
);
}
handle○○ という命名を統一しておくと、コードを読んだときに「どのイベントに対応する処理か」がひと目で分かるようになります。
ハンドラの中身が複雑になってきた場合は、後の記事で扱うカスタムフックへの切り出しも検討していくとよいでしょう。
まとめ
この記事のポイント
- イベントハンドリングは、クリックや入力などの操作をきっかけに処理を実行する仕組み
- Reactでは
onClickのようにキャメルケースで属性を書き、関数そのものを渡す - 引数を渡したいときはアロー関数でラップして
onClick={() => func(arg)}のように書く onClick={handleClick()}と()を付けると即実行されてしまうので注意するonChangeを使えば入力内容の変化を状態と連動させられる
次に読むべき記事
イベントとStateを組み合わせられるようになったら、次は「表示するかしないか」を切り替える条件分岐レンダリングを学んでいきます。
→ 次の記事:条件分岐レンダリングのパターン集