はじめに
Reactでイベントハンドラを使用する際、onClick={handleClick()}ではなくonClick={handleClick}と書く必要がありますが、その理由をきちんと理解していなかったので調べた内容をまとめます。
よくある間違い
onClickイベントにhandleClick()のような 括弧付き関数を渡す という書き方があります。
// 間違った書き方
<button onClick={handleClick()}>クリック</button>
この括弧付き関数を使用すると、以下の問題が発生します。
1. レンダリング時に即座に実行される
コンポーネントがレンダリングされるたびに handleClick() が実行されてしまいます。
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
console.log('クリックされました');
setCount(count + 1);
};
return (
// この時点で handleClick() が実行される
<button onClick={handleClick()}>クリック</button>
);
};
2. 無限ループが発生
状態更新により再レンダリングが発生し、そのたびに handleClick() が実行されるため、無限ループに陥ります。
const MyComponent = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
// 状態を更新
setCount(count + 1);
};
return (
// レンダリング → handleClick()実行 → setCount → 再レンダリング → 無限ループ
<button onClick={handleClick()}>カウント: {count}</button>
);
};
3. イベントオブジェクトが存在しない
イベントハンドラは通常、イベントオブジェクトを引数として受け取ります。レンダリング時に実行されるため、イベントオブジェクトが存在しません。
const handleClick = (e: React.MouseEvent) => {
console.log(e); // undefined
e.preventDefault(); // エラーになる
};
// レンダリング時に実行されるため、イベントオブジェクトが渡されない
<button onClick={handleClick()}>クリック</button>;
4. 戻り値(undefined)が渡される
関数を実行すると、その戻り値(この場合は undefined)が onClick プロパティに渡される。
そのため、実際にボタンをクリックしても何も起こらない。
const handleClick = () => {
console.log('クリック');
};
// handleClick() の戻り値(undefined)が onClick に渡される
// クリックしても何も起こらない
<button onClick={handleClick()}>クリック</button>;
解決策
関数参照を渡す
括弧を付けずに 関数名だけを渡す ことで、問題が解決されます。
const MyComponent = () => {
const handleClick = () => {
console.log('クリックされました');
setCount(count + 1);
};
return (
// ✅ 関数参照を渡す(関数は実行されない)
<button onClick={handleClick}>クリック</button>
);
};
以下にレンダリングからイベント発生までの処理フローを整理します。
1. レンダリング時
- コンポーネント関数が実行される
- handleClick の関数参照が onClick に渡される
- 関数は実行されない(参照だけが保存される)
2. 画面に表示される
3. ユーザーがボタンをクリック
4. React が保存していた関数参照を使用して実行
- handleClick(event) が実行される
- この時点で初めて関数が実行される
※ 括弧付き`handleClick()`だと、手順1で即座に実行されてしまう
補足(関数参照、アロー関数、インライン関数)
イベントハンドラを渡す正しい方法には、以下の3つがあります。
1. 関数参照
関数名をそのまま渡す方法です。最もシンプルで推奨される方法です。
引数なしの場合
<button onClick={handleClick}>クリック</button>
引数ありの場合
館数名だけを渡す方法では、引数を渡すことができません。
引数が必要な場合は、アロー関数または普通の関数宣言を使用します。
// 関数呼び出しになってしまうので、即時実行される
<button onClick={handleClick(id)}>クリック</button>
2. アロー関数
アロー関数を使って新しい関数を作成する方法です。 引数を渡す必要がある場合に使用します。
引数なしの場合
<button onClick={() => handleClick()}>クリック</button>
引数ありの場合
// 引数を渡す
<button onClick={() => handleClick(id)}>クリック</button>
// イベントオブジェクトと引数の両方を渡す
<button onClick={(e) => handleClick(e, id)}>クリック</button>
3. インライン関数(function式)
function キーワードを使って新しい関数を作成する方法です。アロー関数と同じように動作しますが 記述が長く なります。
引数なしの場合
<button onClick={function () {handleClick(); }}>
クリック
</button>
引数ありの場合
// 引数を渡す
<button onClick={function () { handleClick(id); }}>クリック</button>
// イベントオブジェクトと引数の両方を渡す
<button onClick={function (e) { handleClick(e, id); }}>クリック</button>
使い分けの推奨
- 引数が不要な場合 → 関数参照
onClick={handleClick} - 引数が必要な場合 → アロー関数
onClick={() => handleClick(id)}
まとめ
なぜReactのイベントハンドラには「関数を実行した結果」ではなく、「関数そのもの」を渡す必要があるのかについて、それぞれ違いを理解することができたので、正しく扱っていきたいです。