Reactのイベント処理
2025-08-08はじめに
この記事では、Reactにおけるイベント処理について、初学者向けにていねいに解説します。
イベント処理とは「ボタンをクリックしたとき」「テキストを入力したとき」「マウスを重ねたとき」など、ユーザーの操作に対して何かを実行する仕組みです。Reactでのイベント処理は、通常のHTMLと似た書き方ですが、いくつか重要な違いがあります。この記事を読み終えると、クリック・キーボード・マウスイベントを自在に扱えるようになります。
HTMLとReactのイベント処理:何が違うの?
まず、通常のHTMLとReactでの書き方の違いを見てみましょう。
/* ---- HTML の書き方 ---- */
<button onclick="handleClick()">クリック</button>
{/* イベント名が小文字、文字列で関数名を渡す */}
/* ---- React の書き方 ---- */
<button onClick={handleClick}>クリック</button>
{/* イベント名がキャメルケース、関数そのものを{}で渡す */}
違いは3点あります。
- イベント名がキャメルケース:
onclick→onClick、onmouseenter→onMouseEnter - 関数そのものを渡す:文字列ではなく
{handleClick}のように波括弧で関数を渡す - デフォルト動作の防止方法が違う:HTMLの
return falseは効かず、event.preventDefault()を明示的に呼ぶ必要がある
「デフォルト動作の防止方法が違う」とは、HTMLや通常のJavaScriptでは、イベント処理の最後で return false を書くと、リンク移動やフォーム送信などのデフォルト動作を止められる場合があります。
しかしReactでは、return false を書いてもデフォルト動作は停止されません。Reactではイベントを独自の仕組みで管理しているため、動作を止めたい場合は、event.preventDefault()を明示的に呼び出す必要があります。
基本的なクリックイベント
まずは最もよく使う onClick から始めましょう。イベントハンドラー(イベントに反応する関数)を定義して、onClick に渡すだけです。
function ClickExample() {
// ① イベントハンドラー関数を定義
const handleClick = () => {
console.log('ボタンがクリックされました!');
};
// ② onClick に関数を渡す(() をつけないこと!)
return <button onClick={handleClick}>クリックしてください</button>;
}
ここで重要なのが onClick={handleClick} と書く点です。onClick={handleClick()} と () をつけてしまうと、ページが表示された瞬間に関数が実行されてしまいます。
// ❌ 誤り:ページ表示と同時に実行される(クリックを待たない)
<button onClick={handleClick()}>クリック</button>
// ✅ 正しい:クリックされたときだけ実行される
<button onClick={handleClick}>クリック</button>
イベントハンドラーの3つの書き方
イベントハンドラーを定義する方法は主に3通りあります。状況に応じて使い分けましょう。
書き方1:関数宣言で定義する
Reactでは、以下のようにイベント時に実行したい関数を onClick に指定してイベント処理を書きます。
function ButtonComponent() {
function handleClick() {
console.log('クリックされました');
}
return <button onClick={handleClick}>ボタン</button>;
}
書き方2:アロー関数で定義する(最もよく使う)
書き方1と同様に onClick に指定してイベント処理します。違いはアロー関数で定義していることです。
function ButtonComponent() {
const handleClick = () => {
console.log('クリックされました');
};
return <button onClick={handleClick}>ボタン</button>;
}
書き方3:インラインで直接書く(シンプルな1行処理向け)
function ButtonComponent() {
return (
<button onClick={() => console.log('クリックされました')}>
ボタン
</button>
);
}
このコードは、クリックされたときに実行する処理を、その場に直接書いている形です。関数定義を別にせずに、処理が1〜2行程度ならインラインで、それ以上になるなら関数として切り出すのが読みやすくなります。
イベントオブジェクト(event)を使う
イベントハンドラーには、クリックやキー入力に関する詳細情報を持つ「イベントオブジェクト」が自動的に渡されます。引数として受け取ることができます。
function EventObjectExample() {
const handleClick = (event) => {
// event には様々な情報が入っている
console.log('イベントの種類:', event.type); // "click"
console.log('クリックしたX座標:', event.clientX); // 例: 245
console.log('クリックしたY座標:', event.clientY); // 例: 108
console.log('クリックした要素:', event.target); // <button>要素
};
return <button onClick={handleClick}>位置を確認</button>;
}
Reactのイベントオブジェクトは「合成イベント(SyntheticEvent)」と呼ばれる独自のオブジェクトですが、ブラウザのネイティブイベントオブジェクトと同じプロパティを持っているので、普通のDOMのイベントと同じ感覚で使えます。
よく使うイベントの種類
React で頻繁に使うイベントをまとめます。
| イベント名 | 発生するタイミング | よく使う場面 |
|---|---|---|
onClick | クリックしたとき | ボタン、リンク |
onChange | 値が変わったとき | テキスト入力、セレクトボックス |
onSubmit | フォームが送信されたとき | フォーム |
onFocus | フォーカスが当たったとき | 入力欄のハイライト |
onBlur | フォーカスが外れたとき | 入力欄のバリデーション |
onMouseEnter | マウスが要素に入ったとき | ホバー効果 |
onMouseLeave | マウスが要素から出たとき | ホバー効果 |
onKeyDown | キーを押したとき | Enterキーで送信など |
onKeyUp | キーを離したとき | キーボードショートカット |
マウスイベントの例
このコードは、マウスが要素に入った時と出た時のイベントを処理するReactコンポーネントです。onMouseEnter はマウスが <div> に入った時に実行され、コンソールに「マウスが入ってきた」と表示されます。onMouseLeave はマウスが <div> から出た時に実行され、「マウスが出ていった」と表示されます。つまり、「マウス操作に反応するイベント処理」の例です。
function HoverBox() {
const handleMouseEnter = () => console.log('マウスが入ってきた');
const handleMouseLeave = () => console.log('マウスが出ていった');
return (
<div
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
style={{ padding: '20px', backgroundColor: 'lightblue' }}
>
マウスをここに重ねてみてください
</div>
);
}
キーボードイベントの例
function EnterSearch() {
const handleKeyDown = (event) => {
// event.key で押されたキーの名前がわかる
if (event.key === 'Enter') {
console.log('Enterが押されました!検索を実行します');
}
};
return (
<input
type="text"
onKeyDown={handleKeyDown}
placeholder="入力してEnterを押してください"
/>
);
}
このコードは、入力欄でキーが押された時のイベントを処理しています。onKeyDown={handleKeyDown} によって、キーボード入力時に handleKeyDown 関数が実行されます。
その中で、「javascript id=”m551qa” event.key === ‘Enter’」を使い、「押されたキーが Enter かどうか」を判定しています。そのため、入力欄で Enter キーを押すと、「text id=”r882pd” Enterが押されました!検索を実行します」がコンソールに表示されます。つまり、「Enterキーが押されたら特定の処理を実行する」コードです。
イベントとStateの連携
イベント処理の最も重要な使い方は、イベントが起きたときに State を更新して画面を変化させることです。前の記事で学んだ useState と組み合わせることで、インタラクティブなUIが作れます。
クリックカウンター
このコードは、ボタンを押すことで数字を増減できるカウンターコンポーネントです。const [count, setCount] = useState(0);で count というStateを作成し、初期値を 0 にしています。increment は、setCount(count + 1)を実行して、カウントを1増やします。
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// イベントハンドラーの中で State を更新する
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
return (
<div>
<p>現在のカウント: {count}</p>
<button onClick={increment}>増やす</button>
<button onClick={decrement}>減らす</button>
</div>
);
}
decrement は、setCount(count – 1)を実行して、カウントを1減らします。そして、onClick={increment}やonClick={decrement}によって、ボタンがクリックされた時にStateが更新されます。Stateが変わると画面も自動で更新されるため、表示される数字がリアルタイムで変化します。
ボタンごとに異なるメッセージを表示
このコードは、ボタンを押すと表示メッセージが切り替わるReactコンポーネントです。「const [message, setMessage] = useState(‘ボタンをクリックしてください’);」で message というStateを作成し、最初は「ボタンをクリックしてください」が表示されます。
import { useState } from 'react';
function MessageSwitcher() {
const [message, setMessage] = useState('ボタンをクリックしてください');
return (
<div>
<p>{message}</p>
<button onClick={() => setMessage('こんにちは!')}>挨拶</button>
<button onClick={() => setMessage('さようなら!')}>別れ</button>
<button onClick={() => setMessage('ボタンをクリックしてください')}>リセット</button>
</div>
);
}
各ボタンの onClick では setMessage() を使ってStateを変更しています。たとえば、setMessage(‘こんにちは!’)が実行されると、画面の表示が「こんにちは!」に変わります。つまり、「ボタン操作によってStateを変更し、画面表示を切り替える」コードです。
引数を渡すイベントハンドラー
リストの各アイテムのボタンなど、「どのアイテムがクリックされたか」を知りたい場合は、ハンドラーに引数を渡します。このときはインラインのアロー関数で包んで渡します。
function ItemList() {
const items = ['りんご', 'バナナ', 'みかん'];
// item を引数として受け取るハンドラー
const handleItemClick = (itemName) => {
console.log(`${itemName} がクリックされました`);
};
return (
<ul>
{items.map((item) => (
<li key={item}>
{/* アロー関数で包むことで item を引数として渡せる */}
<button onClick={() => handleItemClick(item)}>
{item}
</button>
</li>
))}
</ul>
);
}
このコードは、配列に入っている商品名を一覧表示し、それぞれのボタンをクリックすると、その商品名をコンソールに表示するReactコンポーネントです。まず、りんご・バナナ・みかんという3つのデータを配列として用意しています。
そして map を使って配列の中身を1つずつ取り出し、ボタンの一覧を作成しています。ボタンがクリックされると、クリックされた商品の名前を handleItemClick に渡し、「○○ がクリックされました」とコンソールに表示します。
onClick の中でアロー関数を使っている理由は、クリックされた時に初めて関数を実行し、さらに「どの商品が押されたか」という値を引数として渡すためです。つまりこのコードは、「一覧データからボタンを動的に作り、それぞれのボタンで異なるデータを扱う方法」を表しています。
const handleItemClick = (itemName, event) => {
console.log(`${itemName} がクリックされました`, event.type);
};
// 呼び出し側:e を受け取って両方渡す
<button onClick={(e) => handleItemClick(item, e)}>{item}</button>
イベントオブジェクト(event)と独自の引数を両方使いたいときは、次のように書きます。ボタンがクリックされると、まず onClick の中のアロー関数が実行されます。その時、Reactが自動で渡してくれるイベント情報を e として受け取り、それを handleItemClick に渡しています。
同時に、item も一緒に渡しているため、関数の中では「どの商品がクリックされたか」と「どんなイベントが発生したか」の両方を利用できます。event.type はイベントの種類を表しており、この場合はクリックイベントなので "click" が表示されます。
イベントの伝搬(バブリング)を理解しよう
HTMLのDOMでは、子要素のイベントが親要素にも伝わっていきます(これを「バブリング」と言います)。Reactでも同じ仕組みが適用されます。
Reactのバブリングとは、子要素で発生したイベントが親要素へ順番に伝わっていく仕組みのことです。たとえば、ボタンが <div> の中にある場合、ボタンをクリックすると、「ボタンのクリック処理」→「親の <div> のクリック処理」というようにイベントが上へ伝わります。これをイベントバブリングと呼びます。
以下のようにイベントバブリングが親へ伝わるのを止めたい場合は、stopPropagation() を使います。
function BubblingExample() {
const handleParentClick = () => {
console.log('② 親要素のクリックが発生');
};
const handleChildClick = (e) => {
console.log('① 子要素のクリックが発生');
// e.stopPropagation() を呼ばないと、親にも伝わる
};
return (
<div
onClick={handleParentClick}
style={{ padding: '20px', backgroundColor: 'lightgray' }}
>
親要素
<button onClick={handleChildClick}>子ボタン</button>
</div>
);
}
// 子ボタンをクリックすると、①→② の順に両方が実行される
子のイベントを親に伝えたくない場合は e.stopPropagation() を使います。
const handleChildClick = (e) => {
e.stopPropagation(); // ← これで親への伝搬を止める
console.log('子要素のクリックだけ処理');
};
フォームの送信などブラウザのデフォルト動作を止めたい場合は e.preventDefault() を使います。この場合は「子要素のクリック処理だけを実行し、親要素には影響させない」ためのものです。
const handleSubmit = (e) => {
e.preventDefault(); // ← ページリロードを防ぐ
console.log('フォームを送信しました(ページはリロードされない)');
};
カスタムコンポーネントにイベントを渡す
自分で作ったコンポーネントにもイベントハンドラーを Props として渡せます。onClick という名前で渡すのが一般的です。
// 汎用ボタンコンポーネント:onClick を Props で受け取る
function CustomButton({ onClick, children }) {
return (
<button
onClick={onClick}
style={{
padding: '10px 15px',
backgroundColor: '#4CAF50',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
{children}
</button>
);
}
function App() {
const handleClick = () => {
alert('カスタムボタンがクリックされました!');
};
return (
<CustomButton onClick={handleClick}>
クリックしてください
</CustomButton>
);
}
コンポーネント内でさらに処理を追加してから、Props の関数を呼ぶ「拡張パターン」も便利です。
function LoggingButton({ onClick, children }) {
const handleClick = (event) => {
console.log('ボタンがクリックされた(ログ記録)');
if (onClick) {
onClick(event); // Props の onClick も呼ぶ
}
};
return <button onClick={handleClick}>{children}</button>;
}
実践的なイベント処理の例
ツールチップの実装
onMouseEnter と onMouseLeave を使って、マウスを重ねると説明文が現れるツールチップを作れます。
import { useState } from 'react';
function Tooltip({ text, children }) {
const [isVisible, setIsVisible] = useState(false);
return (
<div style={{ position: 'relative', display: 'inline-block' }}>
<span
onMouseEnter={() => setIsVisible(true)}
onMouseLeave={() => setIsVisible(false)}
>
{children}
</span>
{isVisible && (
<div style={{
position: 'absolute',
bottom: '100%',
left: '50%',
transform: 'translateX(-50%)',
backgroundColor: 'black',
color: 'white',
padding: '5px 10px',
borderRadius: '4px',
whiteSpace: 'nowrap',
}}>
{text}
</div>
)}
</div>
);
}
// 使い方
<Tooltip text="これはツールチップです">
<button>ここにホバーしてください</button>
</Tooltip>
二重送信防止ボタン
ボタンをクリックした後、一定時間無効にすることで連打による二重送信を防げます。
import { useState } from 'react';
function SafeSubmitButton({ onClick, children }) {
const [isDisabled, setIsDisabled] = useState(false);
const handleClick = (event) => {
if (isDisabled) return; // 処理中は何もしない
setIsDisabled(true); // ① ボタンを無効化
onClick(event); // ② 本来の処理を実行
// ③ 1秒後に再び有効化
setTimeout(() => setIsDisabled(false), 1000);
};
return (
<button onClick={handleClick} disabled={isDisabled}>
{isDisabled ? '処理中...' : children}
</button>
);
}
// 使い方
<SafeSubmitButton onClick={() => console.log('送信!')}>
送信する
</SafeSubmitButton>
よくあるエラーと解決策
| エラー・症状 | 原因 | 解決策 |
|---|---|---|
| ページ表示と同時に関数が実行される | onClick={handleClick()} と書いている | onClick={handleClick} に修正する |
event が undefined | onClick={() => handleClick(event)} と書いている | onClick={(e) => handleClick(e)} に修正する |
クラス型で this が undefined | メソッドに this がバインドされていない | アロー関数で定義するか、コンストラクタで bind する |
| 子のクリックが親にも伝わる | イベントバブリングが発生している | 子ハンドラーで e.stopPropagation() を呼ぶ |
| フォームを送信するとページがリロードされる | ブラウザのデフォルト動作が実行されている | e.preventDefault() を呼ぶ |
まとめ
Reactのイベント処理の重要なポイントを整理しておきましょう。
イベント名はキャメルケースで記述し、onClick・onChange・onKeyDown などの形式を用います。イベントハンドラーには関数そのものを渡し、onClick={handleClick} のように記述し、handleClick() のように実行はしません。イベントオブジェクトはハンドラーの引数として受け取ることができ、event.type や event.target などの情報にアクセスできます。また、Stateとの連携として、ハンドラー内で setState を呼び出すことで画面の更新が可能です。引数を渡したい場合は、onClick={() => handle(item)} のようにアロー関数で包みます。さらに、イベントの伝搬は e.stopPropagation() を使うことでバブリングを停止できます。
演習問題
🟢 初級問題(問題1〜3)
イベントハンドラーの基本的な書き方・イベントオブジェクトの使い方・Stateとの基本的な組み合わせを練習します。
問題1:クリックイベントの基本を実装しよう
【問題】
以下の条件をすべて満たす AlertButton コンポーネントを作成してください。
- ボタンに「メッセージを表示」と表示する
- クリックすると
alert("こんにちは!Reactのイベント処理です。")を実行する - ハンドラー関数を
handleClickという名前で コンポーネント内の変数として定義し、onClickに渡すこと(インラインに直接書かないこと)
【期待する動作】
ボタンをクリックするとアラートが表示される。
問題2:イベントオブジェクトの情報を表示しよう
【問題】
以下の条件をすべて満たす ClickInfo コンポーネントを作成してください。
- State として
info(文字列、初期値:"ボタンをクリックしてください")を管理する - ボタンをクリックしたとき、イベントオブジェクトの
clientX・clientYを使って"クリック座標: X=〇〇, Y=〇〇"という文字列をinfoに設定する infoを<p>タグで画面に表示する
【期待する表示(クリック後)】
クリック座標: X=320, Y=214
[ここをクリック]
問題3:キーボードイベントでEnter送信を実装しよう
【問題】
以下の条件をすべて満たす EnterInput コンポーネントを作成してください。
- State として
inputValue(文字列、初期値:"")とsubmitted(文字列、初期値:"")を管理する inputのonChangeでinputValueを更新するonKeyDownでevent.key === "Enter"のとき、inputValueの内容をsubmittedに設定し、inputValueを空にするsubmittedが空でなければ"送信された内容: 〇〇"と表示する
【期待する動作(”React” と入力してEnter後)】
[ ] ← 入力欄は空になる
送信された内容: React
🟡 中級問題(問題4〜9)
引数付きイベント・バブリング制御・カスタムコンポーネントへのイベント渡し・複合的なState連携を練習します。
問題4:リストアイテムに引数付きクリックを実装しよう
【問題】
以下の条件をすべて満たす ColorPalette コンポーネントを作成してください。
const colors = [
{ id: 1, name: "赤", hex: "#e74c3c" },
{ id: 2, name: "青", hex: "#3498db" },
{ id: 3, name: "緑", hex: "#2ecc71" },
{ id: 4, name: "黄", hex: "#f1c40f" },
];
- State として
selected(初期値:null)を管理する - 各色を
<button>で表示し、ボタンの背景色をhexで設定する - ボタンをクリックすると、そのカラーオブジェクトを
selectedにセットする(引数付きアロー関数で渡すこと) selectedがnullでなければ"選択中: 赤(#e74c3c)"のように表示する
【期待する表示(「青」クリック後)】
[赤] [青] [緑] [黄] ← 各ボタンはそれぞれの色
選択中: 青(#3498db)
問題5:バブリングをstopPropagationで制御しよう
【問題】
以下の条件をすべて満たす CardWithButton コンポーネントを作成してください。
- State として
log(文字列配列、初期値:[])を管理する - 外側の
<div>(カード全体)をクリックすると"カード全体がクリックされました"をlogに追加する - カード内部の「詳細を見る」ボタンをクリックすると
"詳細ボタンがクリックされました"をlogに追加するが、カード全体のクリックは発火させない(e.stopPropagation()を使うこと) logの内容を<ul>で一覧表示する
【期待する動作(詳細ボタン→カード全体の順でクリック)】
• 詳細ボタンがクリックされました ← カードには伝播しない
• カード全体がクリックされました
問題6:フォームのデフォルト動作をpreventDefaultで防ごう
【問題】
以下の条件をすべて満たす SearchForm コンポーネントを作成してください。
- State として
query(文字列、初期値:"")とresult(文字列、初期値:"")を管理する <form>タグのonSubmitでe.preventDefault()を呼び、ページリロードを防ぐ- 送信時に
resultを"「〇〇」を検索しました"(〇〇は query の内容)に更新し、queryを空にする resultが空でなければ結果を表示する
【期待する動作(”React” で検索後)】
[ ] [検索]
「React」を検索しました
問題7:カスタムボタンコンポーネントにイベントを渡そう
【問題】
以下の条件をすべて満たすプログラムを作成してください。
IconButtonコンポーネントを作成する。Props としてonClick・icon(絵文字)・label(テキスト)を受け取り、"[icon] label"形式のボタンを表示する- 親コンポーネント
Appで State としてcount(数値、初期値:0)を管理する IconButtonを3つ使い、それぞれ「➕ 増やす」「➖ 減らす」「🔄 リセット」ボタンを実装する- 現在の
countをAppの中で表示する
【期待する表示(初期状態)】
カウント: 0
[➕ 増やす] [➖ 減らす] [🔄 リセット]
問題8:ホバーで背景色が変わるカードを作ろう
【問題】
以下の条件をすべて満たす HoverCard コンポーネントを作成してください。
const cards = [
{ id: 1, title: "Reactの基礎", desc: "JSXとコンポーネントを学ぶ" },
{ id: 2, title: "PropsとState", desc: "データの管理方法を理解する" },
{ id: 3, title: "イベント処理", desc: "インタラクションを実装する" },
];
- State として
hoveredId(数値 or null、初期値:null)を管理する - 各カードの
onMouseEnterでそのカードのidをhoveredIdに設定し、onMouseLeaveでnullに戻す - ホバーされているカードの背景色を
"#e8f4fd"、それ以外は"#fff"にする(インラインスタイルで切り替えること)
問題9:onFocusとonBlurでフォーカス状態を管理しよう
【問題】
以下の条件をすべて満たす FocusInput コンポーネントを作成してください。
- State として
isFocused(真偽値、初期値:false)とvalue(文字列、初期値:"")を管理する onFocusでisFocusedをtrueに、onBlurでfalseに更新する- フォーカス中は入力欄の枠線を
"2px solid #3498db"、非フォーカス時は"1px solid #ccc"にする(style属性で切り替えること) - フォーカス中は入力欄の上に
"✏️ 入力中..."と表示し、フォーカスが外れたら"入力値: 〇〇"(空のときは非表示)を表示する
【期待する表示(フォーカス中・”Hello” 入力)】
✏️ 入力中...
[Hello ] ← 青い枠線
🔴 上級問題(問題10〜12)
複数のイベントを組み合わせた複合的な実装・カスタムコンポーネントの拡張・実践的なUIコンポーネントの設計に挑戦します。
問題10:キーボードショートカット付きカウンターを実装しよう
【問題】
以下の条件をすべて満たす KeyboardCounter コンポーネントを作成してください。
- State として
count(数値、初期値:0)を管理する - 画面上にボタン(「+1」「−1」「リセット」)を表示し、クリックで操作できる
- さらに キーボードでも操作できるようにする:
div要素にonKeyDownを付け、以下のショートカットを実装する
| キー | 動作 |
|---|---|
ArrowUp | +1 |
ArrowDown | −1 |
r | リセット |
- キーボードで操作できるよう、
divにtabIndex={0}を付けること(これでフォーカスを当てられるようになる) - 使えるショートカットの説明を画面に表示すること
問題11:ドラッグ&ドロップ風の並び替えリストを実装しよう
【問題】
マウスイベントを使って、クリックで「選択」→別の位置に「移動」できるリストを実装してください。
const initialItems = ["React", "JavaScript", "TypeScript", "Node.js", "CSS"];
- State として
items(配列)とselectedIndex(数値 or null、初期値:null)を管理する - アイテムをクリックすると
selectedIndexにそのインデックスを設定し、選択中アイテムの背景色を変える selectedIndexが設定済みの状態で別のアイテムをクリックすると、選択中のアイテムをそのアイテムの前に挿入して並び替える(filter()とsplice()または スプレッド構文で新しい配列を作ること)- 並び替え後、
selectedIndexをnullにリセットする
【期待する動作】
1回目クリック: "TypeScript" を選択(ハイライト)
2回目クリック: "React" をクリック → TypeScript が React の前に移動
結果:
• TypeScript ← 移動された
• React
• JavaScript
• Node.js
• CSS
問題12:アコーディオンUIを実装しよう
【問題】
クリックで開閉するアコーディオン(折りたたみ)コンポーネントを実装してください。
const faqs = [
{ id: 1, question: "Reactとは何ですか?", answer: "UIを構築するためのJavaScriptライブラリです。" },
{ id: 2, question: "なぜReactを使うのですか?", answer: "コンポーネントベースの設計で再利用性が高く、効率的な開発が可能です。" },
{ id: 3, question: "Hooksとは何ですか?", answer: "React 16.8で導入された、関数型コンポーネントで状態管理などを行う仕組みです。" },
];
AccordionItemコンポーネント:question・answer・isOpen・onToggleを Props で受け取る。questionをクリックするとonToggleを呼ぶ。isOpenがtrueのときだけanswerを表示するAccordionコンポーネント:openId(数値 or null、初期値:null)を State として管理する。同じ質問を再度クリックすると閉じる(openId === id ? null : id)ように切り替える。faqsをmap()でAccordionItemとして展開する- 一度に開いているのは常に1項目のみ(別の質問を開くと前の質問は閉じる)
【期待する表示(2番目が開いている状態)】
▶ Reactとは何ですか?
▼ なぜReactを使うのですか?
コンポーネントベースの設計で再利用性が高く、効率的な開発が可能です。
▶ Hooksとは何ですか?
📘 解説(全12問)
解説1:クリックイベントの基本を実装しよう
【解答例】
function AlertButton() {
// コンポーネント内に関数を定義
const handleClick = () => {
alert("こんにちは!Reactのイベント処理です。");
};
// onClick に関数を渡す(()は不要)
return <button onClick={handleClick}>メッセージを表示</button>;
}
【解説】
handleClickをコンポーネント内の変数として定義することで、処理の内容がひと目でわかるonClick={handleClick}と書く。handleClick()と書くと即時実行されてしまうため注意- 処理が1行なら
onClick={() => alert("...")}のようにインラインで書くのも有効だが、複数行になるなら関数として切り出す
解説2:イベントオブジェクトの情報を表示しよう
【解答例】
import { useState } from 'react';
function ClickInfo() {
const [info, setInfo] = useState("ボタンをクリックしてください");
const handleClick = (event) => {
// event.clientX / clientY でクリック座標を取得
setInfo(`クリック座標: X=${event.clientX}, Y=${event.clientY}`);
};
return (
<div>
<p>{info}</p>
<button onClick={handleClick}>ここをクリック</button>
</div>
);
}
【解説】
- イベントハンドラーの第1引数に
event(慣例としてeとも書く)を受け取ると、クリック情報にアクセスできる event.clientX / clientYはブラウザのウィンドウ左上からのピクセル座標- テンプレートリテラル(
`文字列${変数}`)を使って座標を文字列に埋め込む
解説3:キーボードイベントでEnter送信を実装しよう
【解答例】
import { useState } from 'react';
function EnterInput() {
const [inputValue, setInputValue] = useState("");
const [submitted, setSubmitted] = useState("");
const handleKeyDown = (event) => {
if (event.key === "Enter" && inputValue.trim() !== "") {
setSubmitted(inputValue.trim());
setInputValue(""); // 入力欄をリセット
}
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="入力してEnterを押してください"
/>
{submitted && <p>送信された内容: {submitted}</p>}
</div>
);
}
【解説】
event.keyでどのキーが押されたかの名前が取得できる。Enterは"Enter"、矢印キーは"ArrowUp"などonChangeで入力値をリアルタイムに State に反映し、onKeyDownで確定処理を行うのが基本パターンinputValue.trim() !== ""で空文字を除外することで、空のままEnterを押しても送信されない
解説4:リストアイテムに引数付きクリックを実装しよう
【解答例】
import { useState } from 'react';
const colors = [
{ id: 1, name: "赤", hex: "#e74c3c" },
{ id: 2, name: "青", hex: "#3498db" },
{ id: 3, name: "緑", hex: "#2ecc71" },
{ id: 4, name: "黄", hex: "#f1c40f" },
];
function ColorPalette() {
const [selected, setSelected] = useState(null);
return (
<div>
<div>
{colors.map((color) => (
<button
key={color.id}
onClick={() => setSelected(color)} // アロー関数で color を引数として渡す
style={{
backgroundColor: color.hex,
color: 'white',
margin: '4px',
padding: '8px 16px',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
{color.name}
</button>
))}
</div>
{selected && (
<p>選択中: {selected.name}({selected.hex})</p>
)}
</div>
);
}
【解説】
onClick={() => setSelected(color)}のようにアロー関数で包むことで、colorを引数として渡せるselectedにオブジェクトごと保存すると、名前・hex など複数の情報を一度に持てて便利- ループ内で動的なスタイルを設定するときは、インラインスタイルにオブジェクトを渡す
解説5:バブリングをstopPropagationで制御しよう
【解答例】
import { useState } from 'react';
function CardWithButton() {
const [log, setLog] = useState([]);
const addLog = (msg) => setLog((prev) => [...prev, msg]);
const handleCardClick = () => {
addLog("カード全体がクリックされました");
};
const handleDetailClick = (e) => {
e.stopPropagation(); // 親へのバブリングを止める
addLog("詳細ボタンがクリックされました");
};
return (
<div>
<div
onClick={handleCardClick}
style={{ border: '1px solid #ccc', padding: '20px', cursor: 'pointer' }}
>
<p>カードのタイトル</p>
<button onClick={handleDetailClick}>詳細を見る</button>
</div>
<ul>
{log.map((entry, i) => <li key={i}>{entry}</li>)}
</ul>
</div>
);
}
【解説】
e.stopPropagation()を呼ぶと、そのイベントが親要素に伝わらなくなる- ログを追加するヘルパー関数
addLogを切り出すと、handleCardClickとhandleDetailClickの両方から使い回せる - 配列 State の更新は
[...prev, 新要素]で行う(直接pushしないこと)
解説6:フォームのデフォルト動作をpreventDefaultで防ごう
【解答例】
import { useState } from 'react';
function SearchForm() {
const [query, setQuery] = useState("");
const [result, setResult] = useState("");
const handleSubmit = (e) => {
e.preventDefault(); // ページリロードを防ぐ
if (query.trim() === "") return;
setResult(`「${query.trim()}」を検索しました`);
setQuery(""); // 入力欄をリセット
};
return (
<div>
<form onSubmit={handleSubmit}>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="検索キーワード"
/>
<button type="submit">検索</button>
</form>
{result && <p>{result}</p>}
</div>
);
}
【解説】
formのonSubmitに渡したハンドラーでe.preventDefault()を呼ぶのはフォーム処理の基本パターンbutton type="submit"を使うと、ボタンクリックでもEnterキーでもonSubmitが発火する- フォーム送信処理を
onClickではなくonSubmitで行うのが推奨。アクセシビリティ的にも正しい実装になる
解説7:カスタムボタンコンポーネントにイベントを渡そう
【解答例】
import { useState } from 'react';
function IconButton({ onClick, icon, label }) {
return (
<button onClick={onClick} style={{ margin: '4px' }}>
{icon} {label}
</button>
);
}
function App() {
const [count, setCount] = useState(0);
return (
<div>
<p>カウント: {count}</p>
<IconButton icon="➕" label="増やす" onClick={() => setCount(count + 1)} />
<IconButton icon="➖" label="減らす" onClick={() => setCount(count - 1)} />
<IconButton icon="🔄" label="リセット" onClick={() => setCount(0)} />
</div>
);
}
【解説】
IconButtonは表示だけを担当し、ロジックは親が持つ設計。コンポーネントの役割が明確になるonClickProps を受け取ってそのまま<button>に渡すだけなので、呼び出し側が自由に処理を決められる- このパターンは「プレゼンテーションコンポーネント」の典型例。前の記事の内容と合わせて復習しよう
解説8:ホバーで背景色が変わるカードを作ろう
【解答例】
import { useState } from 'react';
const cards = [
{ id: 1, title: "Reactの基礎", desc: "JSXとコンポーネントを学ぶ" },
{ id: 2, title: "PropsとState", desc: "データの管理方法を理解する" },
{ id: 3, title: "イベント処理", desc: "インタラクションを実装する" },
];
function HoverCard() {
const [hoveredId, setHoveredId] = useState(null);
return (
<div>
{cards.map((card) => (
<div
key={card.id}
onMouseEnter={() => setHoveredId(card.id)}
onMouseLeave={() => setHoveredId(null)}
style={{
backgroundColor: hoveredId === card.id ? "#e8f4fd" : "#fff",
border: '1px solid #ddd',
borderRadius: '8px',
padding: '16px',
margin: '8px 0',
cursor: 'pointer',
transition: 'background-color 0.2s',
}}
>
<h3>{card.title}</h3>
<p>{card.desc}</p>
</div>
))}
</div>
);
}
【解説】
hoveredIdにホバー中のカードのIDを保存し、各カードのレンダリング時にhoveredId === card.idで比較するonMouseLeaveでnullに戻すことで、ホバーが外れたときに元の色に戻るtransition: 'background-color 0.2s'を加えるとスムーズな色の変化になる
解説9:onFocusとonBlurでフォーカス状態を管理しよう
【解答例】
import { useState } from 'react';
function FocusInput() {
const [isFocused, setIsFocused] = useState(false);
const [value, setValue] = useState("");
return (
<div>
{isFocused && <p>✏️ 入力中...</p>}
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
style={{
border: isFocused ? "2px solid #3498db" : "1px solid #ccc",
padding: '8px',
borderRadius: '4px',
outline: 'none',
}}
placeholder="クリックしてフォーカスしてください"
/>
{!isFocused && value && <p>入力値: {value}</p>}
</div>
);
}
【解説】
onFocusとonBlurはペアで使うことが多い。フォーカス時のハイライトやバリデーションに便利outline: 'none'を設定しておかないと、ブラウザのデフォルトの青枠と自前の枠線が重なってしまう!isFocused && value && ...は「フォーカスが外れている かつ 値が空でない」ときだけ表示する二重条件
解説10:キーボードショートカット付きカウンターを実装しよう
【解答例】
import { useState } from 'react';
function KeyboardCounter() {
const [count, setCount] = useState(0);
const handleKeyDown = (e) => {
if (e.key === "ArrowUp") setCount((prev) => prev + 1);
if (e.key === "ArrowDown") setCount((prev) => prev - 1);
if (e.key === "r") setCount(0);
};
return (
<div
tabIndex={0} // フォーカス可能にする
onKeyDown={handleKeyDown}
style={{ outline: 'none', padding: '16px', border: '1px solid #ddd' }}
>
<p>カウント: {count}</p>
<button onClick={() => setCount((prev) => prev + 1)}>+1</button>
<button onClick={() => setCount((prev) => prev - 1)}>−1</button>
<button onClick={() => setCount(0)}>リセット</button>
<p style={{ color: '#888', fontSize: '0.9em' }}>
ショートカット: ↑ +1 / ↓ −1 / R リセット(要素をクリックしてフォーカス)
</p>
</div>
);
}
【解説】
tabIndex={0}を付けることで通常フォーカスを受け取れないdivもキーボードイベントを受け取れるようになるe.keyの値は"ArrowUp"・"ArrowDown"・"r"などキーの名前。MDNで一覧を確認できる- State 更新に関数形式
prev => prev + 1を使うことで、連続したキー入力でも正確にカウントできる
解説11:ドラッグ&ドロップ風の並び替えリストを実装しよう
【解答例】
import { useState } from 'react';
function SortableList() {
const [items, setItems] = useState(["React", "JavaScript", "TypeScript", "Node.js", "CSS"]);
const [selectedIndex, setSelectedIndex] = useState(null);
const handleItemClick = (index) => {
if (selectedIndex === null) {
// 1回目のクリック:選択
setSelectedIndex(index);
} else {
if (selectedIndex === index) {
// 同じアイテムをクリック:選択解除
setSelectedIndex(null);
return;
}
// 2回目のクリック:並び替え
const newItems = items.filter((_, i) => i !== selectedIndex);
// selectedIndex のアイテムを target の前に挿入
const targetIndex = index < selectedIndex ? index : index - 1;
newItems.splice(targetIndex, 0, items[selectedIndex]);
setItems(newItems);
setSelectedIndex(null);
}
};
return (
<ul style={{ listStyle: 'none', padding: 0 }}>
{items.map((item, index) => (
<li
key={item}
onClick={() => handleItemClick(index)}
style={{
backgroundColor: selectedIndex === index ? "#ffeaa7" : "#fff",
border: '1px solid #ddd',
padding: '8px 16px',
margin: '4px 0',
cursor: 'pointer',
borderRadius: '4px',
}}
>
{selectedIndex === index ? "✅ " : ""}{item}
{selectedIndex === null && " (クリックして選択)"}
{selectedIndex !== null && selectedIndex !== index && " (ここに移動)"}
</li>
))}
</ul>
);
}
【解説】
selectedIndexがnullかどうかで「選択モード」と「移動モード」を切り替える二段階クリックの設計filter()で選択アイテムを除いた新配列を作り、splice()で正しい位置に挿入する。元の配列は変更しない- 挿入位置の計算
index < selectedIndex ? index : index - 1は、選択アイテムを取り除いた後のインデックスのズレを補正している
解説12:アコーディオンUIを実装しよう
【解答例】
import { useState } from 'react';
const faqs = [
{ id: 1, question: "Reactとは何ですか?", answer: "UIを構築するためのJavaScriptライブラリです。" },
{ id: 2, question: "なぜReactを使うのですか?", answer: "コンポーネントベースの設計で再利用性が高く、効率的な開発が可能です。" },
{ id: 3, question: "Hooksとは何ですか?", answer: "React 16.8で導入された、関数型コンポーネントで状態管理などを行う仕組みです。" },
];
// 各項目を担当するコンポーネント
function AccordionItem({ question, answer, isOpen, onToggle }) {
return (
<div style={{ borderBottom: '1px solid #ddd' }}>
<div
onClick={onToggle}
style={{
padding: '12px 16px',
cursor: 'pointer',
display: 'flex',
justifyContent: 'space-between',
fontWeight: 'bold',
}}
>
<span>{question}</span>
<span>{isOpen ? "▼" : "▶"}</span>
</div>
{isOpen && (
<div style={{ padding: '8px 16px 16px', color: '#555' }}>
{answer}
</div>
)}
</div>
);
}
// 全体を管理するコンポーネント
function Accordion() {
const [openId, setOpenId] = useState(null);
const handleToggle = (id) => {
// 同じIDをクリックしたら閉じる、違うIDなら開く
setOpenId(openId === id ? null : id);
};
return (
<div style={{ border: '1px solid #ddd', borderRadius: '8px', overflow: 'hidden' }}>
{faqs.map((faq) => (
<AccordionItem
key={faq.id}
question={faq.question}
answer={faq.answer}
isOpen={openId === faq.id}
onToggle={() => handleToggle(faq.id)}
/>
))}
</div>
);
}
【解説】
openId === id ? null : idが「アコーディオンの心臓部」。同じIDなら閉じ(null)、違うIDなら開く(そのID)AccordionItemはisOpenという真偽値を受け取るだけで、State の管理はAccordionに任せている。この責務の分離が重要onToggle={() => handleToggle(faq.id)}でアロー関数に包むことで、faq.idを引数として渡している