Reactのイベントハンドリングのアンチパターンまとめ!初心者でもわかる注意点
生徒
「Reactでイベントを使っていると動きが遅くなったり、エラーが出たりすることがあります。」
先生
「それはイベントハンドリングのアンチパターンに当たっている可能性があります。よくある間違いを知ることが大切です。」
生徒
「アンチパターンって具体的にどんなことですか?」
先生
「それでは順番に見ていきましょう。初心者でも簡単に理解できるように説明します。」
1. 無名関数を大量に使う
JSX内で直接アロー関数を使うと、毎回レンダリングのたびに新しい関数が作られます。小規模なら問題ありませんが、大規模なアプリではパフォーマンス低下の原因になります。解決策は、イベントハンドラを事前に定義して関数を渡すことです。
<button onClick={() => handleClick()}>クリック</button>
この書き方は簡単ですが、何百個も同じようなボタンがあるとパフォーマンスに影響します。
2. thisの扱いを誤る
クラスコンポーネントでthisを正しくバインドせずにイベントを使うと、undefinedエラーが発生します。コンストラクタでbindするか、アロー関数で定義すると解決できます。
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this);
}
}
間違えるとthisがコンポーネントを指さず、意図した処理ができません。
3. 不要な再レンダリングを発生させる
親コンポーネントで毎回新しい関数を作って子コンポーネントに渡すと、子コンポーネントも再レンダリングされます。React.memoやuseCallbackを使って関数をメモ化すると無駄な再レンダリングを防げます。
const memoizedHandleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
メモ化することで、関数が同じなら再生成されず、パフォーマンスが改善されます。
4. イベント内でstateを直接変更する
Reactではstateを直接変更すると動作が不安定になります。必ずsetState関数やuseStateの更新関数を使いましょう。アンチパターンとして、stateオブジェクトを直接書き換えることがあります。
// NG例
state.count = state.count + 1;
// OK例
setCount(count + 1);
直接書き換えるとReactの再レンダリングが正しく行われません。
5. イベント伝播の制御を忘れる
ネストした要素でクリックイベントを扱う場合、親要素にもイベントが伝わることがあります。不要な伝播はevent.stopPropagation()で防ぎましょう。伝播を放置すると意図しない処理が実行される原因になります。
const handleClick = (event) => {
event.stopPropagation();
console.log("クリックされた");
};
6. フォーム送信時のpreventDefaultを忘れる
フォームボタンを押すとページがリロードされる場合があります。Reactではevent.preventDefault()を使ってデフォルト動作を止める必要があります。忘れるとユーザー体験が損なわれます。
const handleSubmit = (event) => {
event.preventDefault();
console.log("送信処理");
};
7. ポイント整理
Reactのイベントハンドリングでは、無名関数の多用、thisの誤使用、不要な再レンダリング、stateの直接変更、イベント伝播制御の欠如、フォーム送信時のpreventDefault忘れなどがアンチパターンとして挙げられます。これらを避けることで、パフォーマンスが安定し、エラーも減らせます。
まとめ
Reactイベントハンドリングのアンチパターンを総復習
ここまで、Reactのイベントハンドリングにおいて注意すべきアンチパターンについて詳しく見てきました。 Reactは直感的にイベントを扱える反面、書き方を誤るとパフォーマンス低下や予期しない不具合につながることがあります。 特に初心者のうちは、動いているから問題ないと判断してしまいがちですが、 小さな書き方の違いがアプリ全体の安定性や保守性に大きく影響します。 今回紹介した内容は、React開発を続けていく中で何度も遭遇する重要なポイントです。
無名関数をJSX内で多用する書き方は、一見するとシンプルで分かりやすいものです。 しかし、レンダリングのたびに新しい関数が生成されるため、 コンポーネント数やイベント数が増えるほど処理負荷が高くなります。 イベントハンドラは可能な限り事前に定義し、必要に応じてuseCallbackを使うことで、 無駄な関数生成を防ぐことができます。
クラスコンポーネントにおけるthisの扱いも、初心者がつまずきやすいポイントです。 thisのバインドを忘れると、イベント発火時にundefinedエラーが発生し、 意図した処理が実行されません。 現在は関数コンポーネントが主流ですが、既存コードの保守や理解のためにも、 thisの仕組みを正しく理解しておくことは重要です。
また、不要な再レンダリングを引き起こすイベント設計は、 Reactアプリケーションのパフォーマンスを大きく下げる原因になります。 親コンポーネントで毎回新しい関数を作成し、それを子コンポーネントに渡すと、 propsが変わったと判断され再レンダリングが発生します。 React.memoやuseCallbackを適切に使うことで、 必要な部分だけが更新される効率的なUIを実現できます。
stateを直接変更するアンチパターンも、Reactでは特に注意が必要です。 Reactはstateの変更を検知して再レンダリングを行う仕組みのため、 直接値を書き換えると更新が反映されなかったり、動作が不安定になったりします。 必ずsetStateやuseStateの更新関数を使うという基本を、 イベント処理の中でも徹底することが大切です。
さらに、イベント伝播やフォーム送信時のデフォルト動作の制御も、 見落とされがちなポイントです。 stopPropagationを使わずにイベントが親要素へ伝わると、 意図しない処理が同時に実行されることがあります。 また、preventDefaultを忘れるとページリロードが発生し、 入力内容が消えてしまうなど、ユーザー体験に大きな影響を与えます。 これらはすべて、イベントハンドリングを正しく理解していれば防げる問題です。
まとめとしてのサンプルプログラム
最後に、アンチパターンを避けた基本的なイベントハンドリングの例を確認します。 関数を事前に定義し、state更新も正しい方法で行うシンプルな構成です。 実務でもよく使われる形なので、基礎として身につけておきましょう。
import React, { useState, useCallback } from "react";
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount((prev) => prev + 1);
}, []);
return (
<div>
<p>カウント: {count}</p>
<button onClick={handleClick}>増やす</button>
</div>
);
}
export default App;
このように、イベントハンドラを安定させ、state更新を正しく行うことで、 Reactアプリケーションは読みやすく、拡張しやすい構造になります。 アンチパターンを避ける意識を持つことが、長く使えるコードを書く第一歩です。
生徒
「イベントの書き方ひとつで、こんなに影響が出るとは思っていませんでした。 動いているだけでは不十分なんですね。」
先生
「その通りです。Reactでは正しい書き方を積み重ねることで、 パフォーマンスも安定性も大きく変わってきます。」
生徒
「無名関数やstateの直接変更が、アンチパターンだという理由も理解できました。」
先生
「理由を理解できたのは大きな成長ですね。 なぜダメなのかを知ることで、応用力も身についていきます。」
生徒
「これからは、イベントハンドリングを書くときに、 アンチパターンになっていないか意識してみます。」
先生
「その姿勢がとても大切です。 今回学んだ内容を意識すれば、Reactの理解は確実に深まりますよ。」