deve.K

エンジニアが未来を切り開く。

ReactでのonClickイベント処理 【イベントハンドラ】

Reactイベントハンドラ

この記事では、最新のReactでイベントハンドラ関数に引数を渡す方法のイベントハンドラおよびインラインハンドラの処理を説明します。

ボタンやリンク、またはほとんどすべての要素をクリックした後に動作を実行する必要がある場合は常に、onClickイベントハンドラを使用します。

onClickイベントハンドラは、Reactで最も強力で最も使用されている機能の1つです。

本日では、onChangeイベント処理の解説も含めておりますが、基本的にはonClickイベントハンドラでのイベント処理を重点的に学習していきます。

SyntheticEvent(合成イベント)

イベントとは、ユーザーの操作またはシステムが生成したイベントによって発生するトリガー動作のことです。

キーを押す、マウスをクリックする、ウェブページを読み込む、ウィンドウのサイズを変更する、およびその他の対話はイベントの例です。

本来イベント処理は、ブラウザの互換性に問題があるとして知られております。

Reactのイベントハンドラを含む、その他のフレームワークがなければ、ユーザーのあらゆるタッチに反応する優れたレスポンシブWebアプリケーションを構築することはできないでしょう。

イベントハンドリングは、Reactがユーザーによって行われた各動作を確実に追跡するために不可欠となっております。

Reactは、DOM要素がイベントを処理する方法と同様に動作するイベント処理システムを持っています。

Reactは、合成イベントシステムを使用して、Reactアプリとインターフェースの一貫性と高速性を確保します。

つまり、ブラウザとシステム間でイベントを正規化して同じプロパティを持つようにすることで、一貫性を提供します。

この合成イベントにより、モバイルアプリのElectronやiOSAndroidのReact nativeおよびReactを動作させることができるようになりました。

さらに、1つのネイティブイベントから複数の合成イベントを生成できるため、新しいイベントを素早く作成できるようになっています。

合成イベントオブジェクトはさまざまな属性を持っています。

それら、属性の一部をご紹介します。

e.preventDefault()

//例
const handleSubmit = (e) => {
 e.preventdefault();
 console.log("hello");
 }

e.preventDefault()は、ブラウザによるすべてのデフォルト動作を阻止します。

e.stopPropagation()

// 例
const handlestopPropagation = (e) => { 
 e.stopPropagation(); 
 console.log("stopPropagationをクリックした") 

} 

e.stopPropagation()は、子コンポーネントが呼び出されるたびに、親コンポーネントが呼び出されるのを防ぎます。

上記での、( e )は合成イベントです。

Reactでは、( e )は合成イベントなので、これらの合成イベントをW3C仕様に従って定義しているため、ブラウザ間の互換性についてあなたは心配する必要はありません。

bubbles

イベントがバブリングイベントであるか否かを示すtrueまたはfalseを返す。

つまり、イベントをキャンセル可能かどうかをtrueまたはfalseで返します。

currentTarget

ハンドラがアタッチされている要素を示します。

defaultPrevented:preventDefault()によりイベントがキャンセルされたか否かを示すtrueまたはfalseを返す。

eventPhase

イベントがユーザーによって生成された場合はtrueを返し、ブラウザとスクリプトによって生成された場合はfalseを返します。

type

イベントの種類を示す文字列を返します。

さて、この Synthetic(合成イベント)イベントオブジェクトを使う際には気をつけないといけない点があります。

それがイベントプーリングとなります。

イベントプーリング(Event Pooling )

合成イベントオブジェクトはプールされています。

Reactではパフォーマンス上の理由から、イベントハンドラが呼び出されたオブジェクトのすべてのプロパティを無効化し、イベントハンドラに渡された合成イベントインスタンスを再利用できるように準備します。

イベントを非同期で使おうとしても、期待通りの結果は得られないということです。

イベントハンドラ終了後にイベントオブジェクトのプロパティにアクセスする必要がある場合は、e.persist()を使用します。

イベントデータは、イベントプーリングと呼ばれます。

イベントが発生すると、イベントデータがコールバックに送信されます。

その後、オブジェクトは後で使用するためにクリーンアップされます。

これは、イベントデータをStateに保存しておいて、非同期にアクセスしてタイムアウトを実行し、イベントを再評価することはできないのです。

さまざまな合成イベントとのインターフェースは、通常のJavaScriptイベントでの操作と非常に似ていることに注意してください。

Reactでの目標は、たとえば同じプロパティを使用することにより、合成イベントが通常のネイティブイベントとかなり類似したままになることです。

注意:イベントプーリングはReact v17で削除されたため、このルールの適用はReact v16以下を使用するプロジェクトに限定されます。

React合成イベントおよびプーリングについては、このReact Docsリファレンスガイドを参照して下さい。

Reactのイベントハンドラ

まずは、フックを使用しない単純なアプリコンポーネントを作成し例を見てみましょう。

const App = () => {
  function greeting() {
    alert("Hello!");
  }

return (
    <div>
      <button onClick={greeting}>Click me!</button>
    </div>
  );
};

See the Pen React イベントハンドラ by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


上記は、greeting関数と呼ばれる1つの関数とボタンがあり、Reactコンポーネント内のボタンには、greeting()関数を指しているonClickイベントハンドラを紐付けしています。

こうすることで、ボタンをクリックするたびに関数がトリガー(特定の処理)され、アラートで挨拶を表示します。

greetingをトリガーにするには、イベントハンドラの後に波括弧内{ } で呼び出したい関数の名前を渡してあげます。

 <button onClick={greeting}>Click me!</button>

ここで、初心者が引き起こす間違いを示します。

関数の名前のみをイベントハンドラに渡し、名前の後に括弧( )を記述しないことに注意してください。

// ✖︎ 誤
 <button onClick={greeting()}>Click me!</button>

//◎ 正
 <button onClick={greeting}>Click me!</button>

関数名の後の括弧は関数を実行します。

つまり、括弧で関数を渡した場合はコンポーネントレンダリングされるたびに関数が実行されてしまいます。

コンポーネント初めてレンダリングするときにイベントハンドラが呼び出されるのは1回だけです。

関数の戻り値は関数自体ではなくonClick属性で使用されるため、1回おきにクリックしてもイベントハンドラ関数は呼び出されません。

関数が別の関数を返さない限りは、呼び出すものは何もありません。

Reactでのイベントは小文字ではない事に、注意が必要です。

本来のHTMLでは、小文字です。

<button onclick="handleclick()">
Click me
</button>

React のイベントは小文字ではなくcamelCase(キャメルケース)で名付けられています。

// JSX
<button onClick={handleClick}>
Click me
</button>

これはonClickだけではございません。

例えば、forやclass属性も同様です。

本来のHTMLであれば下記のようにします。

<div class="container"></div>

<label for="your_name"></label>

JSXでは大文字が含まれ、classNameとhtmlForとなります。

// JSX

<div className="container"></div>

<label htmlFor="your_name"></label>

なぜこのようにReactでは、明確に区別しているのか疑問に思われる方もいらっしゃるかと思います。

他の記事でも解説したのですが、前述した通りReactでは大文字と小文字が区別されるため、必要に応じて従わなければいけません。

JSに精通している方であれば、『if』や『for』キーワードはオブジェクトのプロパティ名を除いて、変数として宣言できない事をご存知かと思います。

つまり、これらは予約語です。

JSXはHTMLではありません。

そのためJSXのHTML属性に関しては、別のものを使用しなければいけません。

そのためReactチームは新しくhtmlForやclassName属性を用意したのです。

React初心者では、この小文字のonclickによって思い通りに機能しない原因の1つとしてよくありがちなので意識して書いていくようにして下さい。

イベントハンドラをインライン関数として呼び出す

インライン関数は、ReactコンポーネントレンダリングされるときにonClickハンドラ内で定義される関数の事です。

インラインハンドラとも呼ばれる、インラインイベントハンドラはJSXで直接イベントハンドラを使用することにより、多くの新しいオプションを提供します。

関数定義された、コンポーネントはreturn内にあるonClickハンドラ内にあるため、レンダリング時に作成されます。

インライン関数は、主にJSXの外部での関数の宣言を回避するために使用されます。

まず、JSXで関数ステートメントを使用するのは冗長です。

つまりfunction宣言です。

<button onClick={function(){ alert("hello!!") }}>Click me!</button>

したがって、JavaScriptのアロー関数は、インラインハンドラをより簡潔に定義するのに役立ちます。

下記はインラインアロー関数となります。

const App = () => {
return (
    <div>
      <button onClick={() => alert("hello!!")}>Click me!</button>
    </div>
  );
};

See the Pen React インラインのイベントハンドラー by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.

インライン関数にボタンの値を引数として渡す

インライン関数の一般的な使い方は、ボタンの値を引数として渡すことです。

これは、input要素やonChangeイベントハンドラを使う場合にも非常によくあることです。

下記の例をご確認下さい。

onClickイベントハンドラから返される値の( e )に注目してください。

const App = () => {

return (
    <div>
<button type="button" onClick={(e) => alert(e.target.value)}>
         Click me
</button>
    </div>
  );
};

See the Pen React イベントハンドラ 値を引数として渡す by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


先述の合成イベントの解説でも触れた通り、この( e )の値は、SyntheticEvent(合成イベント)と呼ばれるものです。

合成イベントは、JavaScriptプログラマーによって、新たに作成されるイベントオブジェクトです。

すべてのイベントハンドラはSyntheticEventを渡されます。

これは、ターゲット要素の値など、イベントに関連する有用なメタデータを含むオブジェクトです。

上記では、ターゲット要素の値にアクセスし、ブラウザのアラート内に表示しますが、value属性がないため空が通知されます。

それを表示してみましょう。

const App = () => {

return (
    <div>
<button type="button" value="hello!!" onClick={(e) => alert(e.target.value)}>
         Click me
      </button>
    </div>
  );
};

See the Pen React イベントハンドラ 値を引数として渡す 2 by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


上記の『e.target.value』は、実際のボタン要素にあるvalue属性が元になっております。

onClickインラインイハンドラ内で直接合成イベントを使用する例となります。

この場合、ボタンの値は『e.target.value』経由で取得されていますので、メッセージを警告するために使用されたりします。

onChangeでの『event.target.value』を見てみましょう。

onChangeイベントハンドラは、JSXの< input >要素に渡すことができるpropsとなります。

このpropsは、アプリケーションがユーザー入力をリアルタイムでリッスンできるようにReactによって提供されます。

イベントが発生するonChangeとpropsはパラメータ(引数)として渡した関数を呼び出します。

const App = ()=> {

const handleChange = (event) => {
console.log(event.target.value);
  }

return (
    <input
      type="text"
      name="fullName"
      onChange={handleChange}
    />
  );
};

上記では、input要素のonChangeイベントが発生するたびにhandleChange()関数が呼び出されます。

handleChange()関数に渡されるイベントオブジェクトには、入力イベントに関するすべての詳細が含まれています。

下記で実際の出力を確認できます。

https://codepen.io/enjinia_f/pen/VwXbmab

もちろんprops内で、関数宣言も可能です。

 <input
      type="text"
      name="firstName"
      onChange={event => alert("hello!!")}
    />

テキストフィールドに何かを入力するたびに、ReactがonChangeのpropsに渡した関数をトリガーしてくれます。

複数の関数を呼び出す

ボタンをクリックした後、複数の関数を呼び出したいと思われたはずです。

たとえば、コンポーネントの状態を更新すると同時にアラートを表示したりなどです。

ReactのonClickイベントハンドラ内にロジックを書き込むのは、最善の方法とは言えません。

// ✖︎ 
<button type="button" onClick={(e) => {

console.log("hello!");
console.log("Taro");
}
}>Click me</button>

};

viewとロジックコードは、可能な限り切り離す必要があります。

それにより、よりクリーンなモジュラーコードが促進され、必要に応じてViewライブラリを切り替えることができます。

const App = () => {

const greeting = () => {
    alert('hello!!');
  };

const myName = () => {
    alert('Taro');
  };

return (
    <div>
      <button
        onClick={() => {
          greeting();
          myName();
        }}>
        I'm a button
      </button>
    </div>
  );
};

onClick内でインライン関数を使用して、複数の関数を呼び出しています。

この方法も最良の選択肢とは言えませんが、必要な時もございます。

JSX外部で関数を宣言し、複数の関数をonClickで呼び出していきましょう。

まず1つめの関数を宣言し、単純な足し算の結果を返す関数となります。

const App = () => {

  //function1
  const addition = (a, b) => {
    return a + b;
  };

return (
    <div>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

2つ目の関数を宣言します。

下記でも、単純な掛け算(乗算)の結果を返す関数となります。

ボタンがクリックされるたびに、handleClick関数が呼び出され、イベントオブジェクトが渡されます。

const App = () => {

  //function1
const addition = (a, b) => {
    return a + b;
  };

//function2
const multiply = (x, y) => {
    return x * y;
  };

const handleClick = event => {
    console.log(event.target);
    
    alert(addition(5, 5)); // 10

    alert(multiply(5, 5)); // 25
  };

  return (
    <div>
      <button onClick={handleClick}>Click me</button>
    </div>
  );
}

See the Pen React 複数の関数呼び出し イベントハンドラ by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


handleClick関数では、必要なだけ他の関数を呼び出すことができ、イベントオブジェクトにアクセスする必要がある場合は、それらを転送します。

onClickのpropsに関数を渡しているだけなので、関数を呼び出した結果ではないことに注意してください。

handleClick関数を呼び出した結果をonClickのpropsに渡すと、onClick={ handleClick() }の場合ではページが読み込まれるとすぐに関数が呼び出されますが、これは私たちが望んでいることではありません。

イベントハンドラ内の状態を更新

フックを使用して、onClickイベントハンドラ内の状態を更新していく方法を学びましょう。

ReactのonClickイベントハンドラ内でのインライン関数の非常に一般的な使用法は、コンポーネントの状態を更新することです。

useStateフックでカウントアップでのインラインハンドラとなります。

const App = () => {
  const [count, setCount] = useState(0);

return (
<div>
      Count: {count}
<button 
type="button" 
onClick={() => { 
setCount(count + 1);
}}>
Increment
</button>
</div>
  );
};

See the Pen React フックのインラインのイベントハンドラー by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


JSXの外部で、イベントハンドラを定義さているよりもハンドラにパラメータを渡すためにも使用されるのが多いです。

つまり、JSX外部での下記の場合では機能しません。

<button type="button" onClick={handleCount(-1)}>

パラメータ(引数)を渡す必要がある場合は、代わりにアロー関数を使用しアロー関数のインラインハンドラとして機能させます。

const App = () => {
  const [count, setCount] = useState(0);

  const handleCount = (event, num) => {
    setCount(count + num);
  };

  return (
    <div>
      Count: {count}
      <button type="button" onClick={event => handleCount(event, 1)}>
        Increment
      </button>
      <button type="button" onClick={event => handleCount(event, -1)}>
        Decrement
      </button>
    </div>
  );
};

See the Pen React フックでのイベントハンドラー by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


上記では、incrementで数値を増加し、decrementで数値を減少しています。

イベントと引数を並列に渡すことも可能となっています。

開発者は怠惰な人々であるため、JSXの外部で余分な関数宣言を回避するために、インラインイベントハンドラーが使用されることが多々あります。

それによって、ロジックがJSXに移動してしまい、可読性、保守性、エラー発生率が低くなります。

個人的には最初に学んだように、なるべくインラインイベントハンドラなしでJSXをクリーンに保ち、代わりにJSXの外部でイベントハンドラを宣言するのがベストだと思います。

const App = () => {
const [count, setCount] = useState(0);

const handleCount = () => {
setCount(count + 1);
  };

return (
<div>
      Count: {count}
 <button type="button" onClick={handleCount}>
        Increment
 </button>
</div>
  );
};

See the Pen React フックでのイベントハンドラー by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


先述では、フックを使用しない場合でしたが次はフックを使用した、『event.target.value』を解説します。

テキストフィールドに何かしら入力された値を追跡します、onChangeを使用していきます。

Reactの入力フィールドの値を取得するには、入力フィールドの値を追跡するための、状態変数を宣言します。

const [text, setText] = useState('');

そして、onChange入力フィールドにpropsを追加します。

<input
        type="text"
        name="text"
        onChange={handleChange}
        value={text} 
/>

『event.target.value』は入力フィールドの値を取得し、状態変数を更新するために使用します。

const handleChange = event => { 
setText(event.target.value);

console.log('value is:', event.target.value);
  };

全体図コードと出力は下記で確認できます。

See the Pen React フック onChange event.target.value by dev.K | Webアプリ開発者 (@enjinia_f) on CodePen.


これは、テキストフィールドにonChangeプロパティを設定し、値が変わるたびにhandleChange関数が呼び出されるようになっています。

useStateフックを使用し、入力フィールドの値を追跡する方法となります。

この場合でのコンポーネントは、制御されたコンポーネントと呼びます。

制御・非制御と言います。

つまり制御されたコンポーネントでは、要素の参照を介して入力の値にアクセスするのではなく、値をReact状態で保存できます。

一方で非制御コンポーネントは、従来のHTMLフォーム入力要素に非常に良く似ており

入力への参照にアクセスすることで、入力の値を取得します。

フォーム要素とデータの制御は制限され内部の状態を維持し、データは通常のDOM自体によって制御される事になります。

つまり、ReactDOMの外部で管理します。

非制御コンポーネントについては、下記で学ぶ事ができます。

dev-k.hatenablog.com

useStateフックを使用した、イベントハンドラをコールバックで子から親コンポーネントへデータを渡したいと思われた方は、当ブログの『React propsの渡し方』チュートリアルで解説しておりますのでそちらを参照してください。

dev-k.hatenablog.com

最後に

これらイベントハンドラは、onClickイベントハンドラが渡されたDOM要素をクリックした後にトリガーする必要がある動作を決定するために使用されます

イベントハンドラにはすべて特定の目的があります。

つまり、イベントハンドラはイベントが発生したときにどのような動作をさせるかを決定する事です。

あなたが目標とする事は、なるべくコードを読みやすくそして維持可能に保つことであるべきです。

関数コンポーネントにおけるonClick、onChangeイベントハンドラおよびインラインハンドラの一般的な使用例として、フック、状態の更新、複数の関数の呼び出し、合成イベント、入力フィールドの使用についてあなたは学習しました。

本日は以上となります。

この記事を最後まで読んで頂きありがとうございます。

プライバシーポリシー