ReactでDOMとウィンドウイベントを処理する方法

提供:Dev Guides
移動先:案内検索

著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

Web開発では、eventsはWebブラウザーで発生するアクションを表します。 イベントハンドラーを使用してイベントに応答することにより、マウスでのクリック、Webページのスクロール、タッチスクリーンのタッチなど、あらゆるユーザーアクションに応答する動的なJavaScriptアプリケーションを作成できます。もっと。

React アプリでは、イベントハンドラーを使用して、状態データを更新したり、 prop の変更をトリガーしたり、デフォルトのブラウザーアクションを防止したりできます。 これを行うために、Reactはネイティブのイベントインターフェースの代わりにSyntheticEventラッパーを使用します。 SyntheticEventは、標準のブラウザーイベントを厳密にエミュレートしますが、さまざまなWebブラウザーに対してより一貫した動作を提供します。 Reactは、コンポーネントが Document Object Model(DOM)にマウントおよびアンマウントするときに、 Window イベントリスナーを安全に追加および削除するツールも提供し、[X197Xを制御できるようにします。 ]不適切に削除されたリスナーからのメモリリークを防止しながらのイベント。

このチュートリアルでは、Reactでイベントを処理する方法を学びます。 自己検証型の入力コンポーネントや入力フォームの有益なツールチップなど、ユーザーイベントを処理するいくつかのサンプルコンポーネントを作成します。 チュートリアル全体を通して、コンポーネントにイベントハンドラーを追加する方法、SyntheticEventから情報を取得する方法、およびWindowイベントリスナーを追加および削除する方法を学習します。 このチュートリアルを終えると、さまざまなイベントハンドラーを操作して、Reactでサポートされているイベントのカタログを適用できるようになります。

前提条件

ステップ1—SyntheticEventを使用してイベントデータを抽出する

このステップでは、<input>HTML要素とonChangeイベントハンドラーを使用して検証コンポーネントを作成します。 このコンポーネントは、入力を受け入れて検証するか、コンテンツが特定のテキストパターンに準拠していることを確認します。 SyntheticEventラッパーを使用して、イベントデータをコールバック関数に渡し、<input>からのデータを使用してコンポーネントを更新します。 また、SyntheticEventからfunctionspreventDefaultなど)を呼び出して、標準のブラウザーアクションを防止します。

Reactでは、イベントリスナーを追加する前に要素を選択する必要はありません。 代わりに、小道具を使用してJSXにイベントハンドラーを直接追加します。 React には、onClickonChangeなどの一般的なイベントや、onWheelなどのあまり一般的でないイベントなど、でサポートされるイベントが多数あります。

ネイティブDOMoneventハンドラーとは異なり、ReactはSyntheticEventと呼ばれる特別なラッパーをネイティブブラウザーEventではなくイベントハンドラーに渡します。 抽象化は、ブラウザー間の不整合を減らすのに役立ち、コンポーネントにイベントを操作するための標準インターフェースを提供します。 SyntheticEventのAPIは、ネイティブのEventに似ているため、ほとんどのタスクは同じ方法で実行されます。

これを実証するために、検証入力を行うことから始めます。 まず、FileNamerというコンポーネントを作成します。 これは、ファイルに名前を付けるための入力を持つ<form>要素になります。 入力を入力すると、コンポーネントの上にあるプレビューボックスが更新される情報が表示されます。 コンポーネントには、検証を実行するための送信ボタンも含まれますが、この例では、フォームは実際には何も送信しません。

まず、ディレクトリを作成します。

mkdir src/components/FileNamer

次に、テキストエディタでFileNamer.jsを開きます。

nano src/components/FileNamer/FileNamer.js

FileNamer.js内に、ラッパー<div>を作成してから、クラス名がpreviewのラッパー<div>とラッパー内の<form>要素を追加します。次のコード行を記述します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React from 'react';

export default function FileNamer() {
  return(
    <div className="wrapper">
      <div className="preview">
      </div>
      <form>
      </form>
    </div>
  )
}

次に、プレビューボックスに表示する名前の入力要素と保存ボタンを追加します。 次の強調表示された行を追加します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React from 'react';

export default function FileNamer() {
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview:</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input name="name" />
        </label>
        <div>
          <button>Save</button>
        </div>
      </form>
    </div>
  )
}

preview <div>で、テキストPreviewを含む<h2>要素を追加しました。 これがプレビューボックスになります。 フォーム内に、Name:をテキストとして持つ<label>要素で囲まれた<input>を追加しました。 次に、<form>終了タグの直前に、Saveというbuttonを追加しました。

ファイルを保存して閉じます。

次に、App.jsを開きます。

nano src/components/App/App.js

FileNamerをインポートしてから、次の強調表示された行を追加して、App関数内にレンダリングします。

イベント-tutorial/src / components / App / App.js

import React from 'react';
import FileNamer from '../FileNamer/FileNamer';

function App() {
    return <FileNamer />
}

export default App;

ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、コンポーネントが表示されます。

次に、セクションを定義し、要素にパディングとマージンを追加するために、いくつかの軽いスタイルを追加します。

テキストエディタでFileNamer.cssを開きます。

nano src/components/FileNamer/FileNamer.css

.previewクラスに灰色の境界線とパディングを付けてから、.wrapperクラスに少量のパディングを付けます。 flexflex-directionを使用して列の項目を表示し、すべてのテキストを左揃えにします。 最後に、境界線を削除して黒い境界線を追加することにより、デフォルトのボタンスタイルを削除します。

イベント-tutorial/src / components / FileNamer / FileNamer.css

.preview {
    border: 1px darkgray solid;
    padding: 10px;
}

.wrapper {
    display: flex;
    flex-direction: column;
    padding: 20px;
    text-align: left;
}

.wrapper button {
    background: none;
    border: 1px black solid;
    margin-top: 10px;
}

ファイルを保存して閉じます。 次に、FileNamer.jsを開きます。

nano src/components/FileNamer/FileNamer.js

スタイルをインポートして、コンポーネントに適用します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React from 'react';
import './FileNamer.css';

export default function FileNamer() {
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview:</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input name="name" />
        </label>
        <div>
          <button>Save</button>
        </div>
      </form>
    </div>
  )
}

ファイルを保存します。 これを行うと、ブラウザが更新され、コンポーネントに新しいスタイルが追加されていることがわかります。

基本的なコンポーネントができたので、<input>要素にイベントハンドラーを追加できます。 ただし、最初に、入力フィールドにデータを保存する場所が必要になります。 useState Hook を追加して、入力を保持します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input name="name" />
        </label>
        <div>
          <button>Save</button>
        </div>
      </form>
    </div>
  )
}

このコードでは、useStateを変数nameに分解して入力を保持し、setNameという関数でデータを更新しました。 次に、プレビューセクションにnameを表示し、続いて.js拡張子を表示して、ユーザーがファイルに名前を付けているかのようにしました。

入力データを保存できるようになったので、<input>コンポーネントにイベントハンドラーを追加できます。 多くの場合、特定のタスクに使用できるいくつかの異なるイベントハンドラーがあります。 この場合、アプリはユーザーが要素に入力したデータをキャプチャする必要があります。 この状況で最も一般的なハンドラーはonChangeで、コンポーネントが変更されるたびに起動します。 ただし、onKeyDownonKeyPressonKeyUpなどのキーボードイベントを使用することもできます。 違いは主に、イベントが発生するタイミングと、SyntheticEventオブジェクトに渡される情報に関係しています。 たとえば、要素がフォーカスされなくなったときのイベントであるonBlurは、onClickの前に発生します。 別のイベントが発生する前にユーザー情報を処理する場合は、以前のイベントを選択できます。

イベントの選択は、SyntheticEventに渡すデータのタイプによっても決まります。 たとえば、onKeyPressイベントには、ユーザーが押したキーのcharCodeが含まれますが、onChangeには特定の文字コードは含まれませんが、完全な入力が含まれます。 。 これは、ユーザーが押したキーに応じて異なるアクションを実行する場合に重要です。

このチュートリアルでは、onChangeを使用して、最新のキーだけでなく、入力値全体をキャプチャします。 これにより、変更のたびに値を格納して連結する手間が省けます。

eventを引数として取る関数を作成し、onChangeプロパティを使用して<input>要素に渡します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input name="name" onChange={event => {}}/>
        </label>
        <div>
          <button>Save</button>
        </div>
      </form>
    </div>
  )
}

前述のように、ここでのeventはネイティブブラウザイベントではありません。 これはReactが提供するSyntheticEventであり、同じように扱われることがよくあります。 まれに、ネイティブイベントが必要な場合は、SyntheticEventnativeEvent属性を使用できます。

イベントができたので、イベントのtarget.valueプロパティから現在の値を引き出します。 値をsetNameに渡して、プレビューを更新します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input
           autoComplete="off"
           name="name"
           onChange={event => setName(event.target.value) }
         />
        </label>
        <div>
          <button>Save</button>
        </div>
      </form>
    </div>
  )
}

さらに、属性 autoComplete"off"に設定して、ブラウザーの提案をオフにします。

ファイルを保存します。 これを行うと、ページがリロードされ、<input>と入力すると、プレビューに更新が表示されます。

注:event.target.nameを使用して入力の名前にアクセスすることもできます。 これは、nameがコンポーネントのname属性と自動的に一致するため、複数の入力で同じイベントハンドラーを使用している場合に役立ちます。


この時点で、作業中のイベントハンドラーがあります。 ユーザー情報を取得して状態に保存し、データで別のコンポーネントを更新しています。 ただし、イベントから情報を取得するだけでなく、フォームの送信を防止したり、キー押下アクションを防止したりする場合など、イベントを停止する必要がある場合があります。

イベントを停止するには、イベントでpreventDefaultアクションを呼び出します。 これにより、ブラウザはデフォルトの動作を実行できなくなります。

FileNamerコンポーネントの場合、アプリで禁止するファイルを選択するプロセスを中断する可能性のある特定の文字があります。 たとえば、*はワイルドカード文字と競合するため、ユーザーがファイル名に追加することは望ましくありません。ワイルドカード文字は、別のファイルセットを参照していると解釈される可能性があります。 ユーザーがフォームを送信する前に、無効な文字がないことを確認する必要があります。 無効な文字がある場合は、ブラウザによるフォームの送信を停止し、ユーザーへのメッセージを表示します。

まず、alert boolean関数とsetAlert関数を生成するフックを作成します。 次に、<div>を追加して、alertがtrueの場合にメッセージを表示します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  const [alert, setAlert] = useState(false);

  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input
            autoComplete="off"
            name="name"
            onChange={event => setName(event.target.value) }
          />
        </label>
        {alert && <div> Forbidden Character: *</div>}
        <div>
          <button>Save</button>
        </div>
      </form>
    </div>
  )
}

このコードでは、alertが最初にtrueと等しく設定されている場合にのみ、&&演算子を使用して新しい<div>を表示しました。 <div>のメッセージは、*文字が入力に許可されていないことをユーザーに通知します。

次に、validateという関数を作成します。 正規表現.testメソッドを使用して、文字列に*が含まれているかどうかを確認します。 その場合、フォームの送信を防ぐことができます。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  const [alert, setAlert] = useState(false);
  const validate = event => {
    if(/\*/.test(name)) {
      event.preventDefault();
      setAlert(true);
      return;
    }
      setAlert(false);
 };

  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input
            autoComplete="off"
            name="name"
            onChange={event => setName(event.target.value) }
          />
        </label>
        {alert && <div> Forbidden Character: *</div>}
        <div>
          <button onClick={validate}>Save</button>
        </div>
      </form>
    </div>
  )
}

validate関数が呼び出され、テストがtrueを返す場合、event.preventDefaultを使用してから、setAlert(true)を呼び出します。 それ以外の場合は、setAlert(false)を呼び出します。 コードの最後の部分で、onClickを使用して<button>要素にイベントハンドラーを追加しました。

ファイルを保存します。 以前と同様に、onMouseDownを使用することもできますが、onClickの方が一般的であるため、予期しない副作用を回避できます。 このフォームには送信アクションはありませんが、デフォルトのアクションを禁止することで、ページが再読み込みされないようにします。

これで、onChangeonClickの2つのイベントハンドラーを使用するフォームができました。 イベントハンドラーを使用して、ユーザーアクションをコンポーネントとアプリケーションに接続し、インタラクティブにします。 そうすることで、DOM要素にイベントを追加する方法と、同じアクションで発生するがSyntheticEventで異なる情報を提供するイベントがいくつかあることを学びました。 また、SyntheticEventから情報を抽出し、そのデータを状態に保存して他のコンポーネントを更新し、preventDefaultを使用してイベントを停止する方法も学びました。

次のステップでは、さまざまなユーザーアクションを処理するために、単一のDOM要素に複数のイベントを追加します。

ステップ2—同じ要素に複数のイベントハンドラーを追加する

1つのコンポーネントで複数のイベントが発生する場合があり、1つのコンポーネントでさまざまなイベントに接続できる必要があります。 たとえば、このステップでは、onFocusおよびonBlurイベントハンドラーを使用して、コンポーネントに関するジャストインタイム情報をユーザーに提供します。 このステップを終えると、Reactでサポートされているさまざまなイベントと、それらをコンポーネントに追加する方法について詳しく知ることができます。

validate関数は、フォームが不正なデータを送信するのを防ぐのに役立ちますが、ユーザーエクスペリエンスにはあまり役立ちません。ユーザーは、フォーム全体に入力した後にのみ有効な文字に関する情報を受け取ります。 複数のフィールドがある場合、最後のステップまでユーザーにフィードバックは提供されません。 このコンポーネントをより使いやすくするには、onFocusイベントハンドラーを追加して、ユーザーがフィールドに入力するときに許可される文字と許可されない文字を表示します。

まず、alert <div>を更新して、許可されている文字に関する情報を含めます。 ユーザーに英数字が許可され、*は許可されていないことを伝えます。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
...
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input
            autocomplete="off"
            name="name"
            onChange={event => setName(event.target.value) }
          />
        </label>
        {alert &&
         <div>
           <span role="img" aria-label="allowed">✅</span> Alphanumeric Characters
           <br />
           <span role="img" aria-label="not allowed">⛔️</span> *
         </div>
       }
        <div>
          <button onClick={validate}>Save</button>
        </div>
      </form>
    </div>
  )
}

このコードでは、 Accessible Rich Internet Applications(ARIA)標準を使用して、コンポーネントをスクリーンリーダーでよりアクセスしやすくしました。

次に、<input>要素に別のイベントハンドラーを追加します。 入力をクリックまたはタブで入力してコンポーネントをアクティブ化すると、許可されている文字と許可されていない文字についてユーザーに警告します。 次の強調表示された行を追加します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
...
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input
            autocomplete="off"
            name="name"
            onChange={event => setName(event.target.value) }
            onFocus={() => setAlert(true)}
          />
        </label>
        {alert &&
          <div>
            <span role="img" aria-label="allowed">✅</span> Alphanumeric Characters
            <br />
            <span role="img" aria-label="not allowed">⛔️</span> *
          </div>
        }
        <div>
          <button onClick={validate}>Save</button>
        </div>
      </form>
    </div>
  )
}

onFocusイベントハンドラーを<input>要素に追加しました。 このイベントは、ユーザーがフィールドを選択したときにトリガーされます。 イベントハンドラーを追加した後、setAlert(true)を呼び出してデータを表示する無名関数をonFocusに渡しました。 この場合、SyntheticEventからの情報は必要ありません。 ユーザーが行動したときにのみイベントをトリガーする必要があります。 ReactはまだSyntheticEventを関数に送信していますが、現在の状況では、その中の情報を使用する必要はありません。

注: onClickまたはonMouseDownでデータ表示をトリガーできますが、キーボードを使用してフォームフィールドにタブで移動するユーザーはアクセスできません。 この場合、onFocusイベントは両方のケースを処理します。


ファイルを保存します。 これを行うと、ブラウザが更新され、ユーザーが入力をクリックするまで情報が非表示のままになります。

フィールドにフォーカスがあるときにユーザー情報が表示されるようになりましたが、データはコンポーネントの期間中存在します。 それをなくす方法はありません。 幸い、onBlurと呼ばれる別のイベントがあり、ユーザーが入力を離れると発生します。 alertfalseに設定する匿名関数を使用してonBlurイベントハンドラーを追加します。 onFocusと同様に、これはユーザーがクリックしたときとユーザーがタブで離れたときの両方で機能します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
...
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input
            autocomplete="off"
            name="name"
            onBlur={() => setAlert(false)}
            onChange={event => setName(event.target.value) }
            onFocus={() => setAlert(true)}
          />
        </label>
        {alert &&
          <div>
            <span role="img" aria-label="allowed">✅</span> Alphanumeric Characters
            <br />
            <span role="img" aria-label="not allowed">⛔️</span> *
          </div>
        }
        <div>
          <button onClick={validate}>Save</button>
        </div>
      </form>
    </div>
  )
}

ファイルを保存します。 これを行うと、ブラウザが更新され、ユーザーが要素をクリックすると情報が表示され、ユーザーがクリックすると情報が消えます。

要素に必要な数のイベントハンドラーを追加できます。 必要なイベントのアイデアはあるが名前がわからない場合は、サポートされているイベントをスクロールすると、必要なものが見つかる可能性があります。

このステップでは、単一のDOM要素に複数のイベントハンドラーを追加しました。 さまざまなイベントハンドラーが、クリックとタブの両方などの幅広いイベント、または狭い範囲のイベントをどのように処理できるかを学びました。

次のステップでは、グローバルイベントリスナーをWindowオブジェクトに追加して、直接のコンポーネントの外部で発生するイベントをキャプチャします。

ステップ3—ウィンドウイベントの追加

このステップでは、ユーザー情報をポップアップコンポーネントに配置します。このポップアップコンポーネントは、ユーザーが入力にフォーカスするとアクティブになり、ユーザーがページ上の他の場所をクリックすると閉じます。 この効果を実現するには、 useEffect Hook を使用して、グローバルイベントリスナーをWindowオブジェクトに追加します。 また、コンポーネントがアンマウントされたとき、メモリリークを防ぐため、アプリが必要以上のメモリを消費したときに、イベントリスナーを削除します。

この手順を完了すると、個々のコンポーネントでイベントリスナーを安全に追加および削除できるようになります。 また、useEffectフックを使用して、コンポーネントがマウントおよびアンマウントされるときにアクションを実行する方法についても学習します。

ほとんどの場合、JSXのDOM要素にイベントハンドラーを直接追加します。 これにより、コードに焦点が当てられ、コンポーネントがWindowオブジェクトを介して別のコンポーネントの動作を制御しているという混乱した状況を防ぐことができます。 ただし、グローバルイベントリスナーを追加する必要がある場合があります。 たとえば、スクロールリスナーで新しいコンテンツをロードしたり、コンポーネントの外部でクリックイベントをキャプチャしたりできます。

このチュートリアルでは、ユーザーが特に要求した場合にのみ、入力に関する情報をユーザーに表示します。 情報を表示した後、ユーザーがコンポーネントの外部のページをクリックするたびに情報を非表示にする必要があります。

開始するには、alertディスプレイをinformation-wrapperclassNameを持つ新しい<div>に移動します。 次に、informationclassNamesetAlert(true)を呼び出すonClickイベントを含む新しいボタンを追加します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
...
  return(
    <div className="wrapper">
      <div className="preview">
        <h2>Preview: {name}.js</h2>
      </div>
      <form>
        <label>
          <p>Name:</p>
          <input
            autocomplete="off"
            name="name"
            onChange={event => setName(event.target.value) }
          />
        </label>
        <div className="information-wrapper">
          <button
            className="information"
            onClick={() => setAlert(true)}
            type="button"
          >
            more information
          </button>
         {alert &&
           <div className="popup">
             <span role="img" aria-label="allowed">✅</span> Alphanumeric Characters
             <br />
             <span role="img" aria-label="not allowed">⛔️</span> *
           </div>
         }
        </div>
        <div>
          <button onClick={validate}>Save</button>
        </div>
      </form>
    </div>
  )
}

また、onFocusおよびonBlurハンドラーを<input>要素から削除して、最後のステップからの動作を削除しました。

ファイルを保存して閉じます。 次に、FileNamer.cssを開きます。

nano src/components/FileNamer/FileNamer.css

popup情報をボタンの上に完全に配置するために、スタイルを追加します。 次に、informationのクラスを持つ<button>を、境界線のない青色に変更します。

イベント-tutorial/src / components / FileNamer / FileNamer.css

.information {
   font-size: .75em;
   color: blue;
   cursor: pointer;
}

.wrapper button.information {
    border: none;
}

.information-wrapper {
   position: relative;
}

.popup {
    position: absolute;
    background: white;
    border: 1px darkgray solid;
    padding: 10px;
    top: -70px;
    left: 0;
}

.preview {
    border: 1px darkgray solid;
    padding: 10px;
}

.wrapper {
    display: flex;
    flex-direction: column;
    padding: 20px;
    text-align: left;
}

.wrapper button {
    background: none;
    border: 1px black solid;
    margin-top: 10px;
}

ファイルを保存して閉じます。 これを行うと、ブラウザがリロードされ、more informationをクリックすると、コンポーネントに関する情報が表示されます。

これでポップアップをトリガーできますが、それをクリアする方法はありません。 この問題を修正するには、ポップアップの外側のクリックでsetAlert(false)を呼び出すグローバルイベントリスナーを追加します。

イベントリスナーは次のようになります。

window.addEventListener('click', () => setAlert(false))

ただし、コードでイベントリスナーを設定するときは注意が必要です。 たとえば、コンポーネントコードの先頭にイベントリスナーを追加することはできません。これは、何かが変更されるたびに、コンポーネントが再レンダリングされて新しいイベントリスナーが追加されるためです。 コンポーネントは何度も再レンダリングされる可能性が高いため、メモリを消費する未使用のイベントリスナーが多数作成されます。

これを解決するために、ReactにはuseEffectと呼ばれる特別なフックがあり、特定のプロパティが変更された場合にのみ実行されます。 基本的な構造は次のとおりです。

useEffect(() => {
 // run code when anything in the array changes
}, [someProp, someOtherProp])

簡略化された例では、somePropまたはsomeOtherPropが変更されるたびに、Reactは無名関数でコードを実行します。 配列内の項目は依存関係と呼ばれます。 このフックは、依存関係の変更をリッスンし、変更後に関数を実行します。

これで、useEffectを使用して、alerttrueの場合はいつでもイベントリスナーを追加し、alertの場合はいつでも削除することで、グローバルイベントリスナーを安全に追加および削除するツールができました。 ]はfalseです。

もう1つのステップがあります。 コンポーネントがアンマウントされると、useEffectフックの内側から戻ってきた関数が実行されます。 このため、コンポーネントがアンマウントされたときにイベントリスナーを削除する関数も返す必要があります。

基本的な構造は次のようになります。

useEffect(() => {
 // run code when anything in the array changes
  return () => {} // run code when the component unmounts
}, [someProp, someOtherProp])

useEffectフックの形状がわかったので、アプリケーションで使用します。 FileNamer.jsを開きます:

nano src/components/FileNamer/FileNamer.js

内部で、useEffectをインポートし、関数の後に配列にalertsetAlertの依存関係を持つ空の無名関数を追加します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useEffect, useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  const [alert, setAlert] = useState(false);

  useEffect(() => {
  }, [alert, setAlert]);
...

このコードでは、alertsetAlertの両方を追加しました。 完了するために、Reactはすべての外部依存関係をuseEffect関数に追加することをお勧めします。 setAlert関数を呼び出すため、依存関係と見なすことができます。 setAlertは最初のレンダリング後に変更されませんが、依存関係と見なされる可能性のあるものをすべて含めることをお勧めします。

次に、無名関数内に、setAlert(false)を呼び出すhandleWindowClickという新しい関数を作成します。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useEffect, useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  const [alert, setAlert] = useState(false);

  useEffect(() => {
    const handleWindowClick = () => setAlert(false)
  }, [alert, setAlert]);
  ...
}

次に、alerttrueの場合にwindow.addEventListener('click', handleWindowClick)を呼び出し、alertの場合にwindow.removeEventListener('click', handleWindowClick)を呼び出す条件付きを追加します。 false。 これにより、ポップアップをトリガーするたびにイベントリスナーが追加され、ポップアップが閉じられるたびにイベントリスナーが削除されます。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useEffect, useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  const [alert, setAlert] = useState(false);

  useEffect(() => {
    const handleWindowClick = () => setAlert(false)
    if(alert) {
      window.addEventListener('click', handleWindowClick);
    } else {
      window.removeEventListener('click', handleWindowClick);
    }
  }, [alert, setAlert]);
  ...
}

最後に、イベントリスナーを削除する関数を返します。 繰り返しますが、これはコンポーネントがアンマウントされたときに実行されます。 ライブイベントリスナーがない場合もありますが、リスナーがまだ存在する状況では、クリーンアップする価値があります。

イベント-tutorial/src / components / FileNamer / FileNamer.js

import React, { useEffect, useState } from 'react';
import './FileNamer.css';

export default function FileNamer() {
  const [name, setName] = useState('');
  const [alert, setAlert] = useState(false);

  useEffect(() => {
    const handleWindowClick = () => setAlert(false)
    if(alert) {
      window.addEventListener('click', handleWindowClick);
    } else {
      window.removeEventListener('click', handleWindowClick)
    }
    return () => window.removeEventListener('click', handleWindowClick);
  }, [alert, setAlert]);
  ...
}

ファイルを保存します。 これを行うと、ブラウザが更新されます。 詳細情報ボタンをクリックすると、メッセージが表示されます。 開発者ツールでグローバルイベントリスナーを見ると、clickリスナーがあることがわかります。

コンポーネントの外側をクリックします。 メッセージが消え、グローバルクリックイベントリスナーが表示されなくなります。

useEffectフックは、ユーザーの操作に基づいてグローバルイベントリスナーを正常に追加および削除しました。 特定のDOM要素に関連付けられていませんでしたが、代わりにコンポーネントの状態の変更によってトリガーされました。

注:アクセシビリティの観点から、このコンポーネントは完全ではありません。 ユーザーがマウスを使用できない場合、コンポーネントの外側をクリックすることはできないため、ポップアップが開いたままになります。 解決策は、keydownに別のイベントリスナーを追加して、メッセージも削除することです。 メソッドがclickではなくkeydownになることを除いて、コードはほぼ同じです。


このステップでは、コンポーネント内にグローバルイベントリスナーを追加しました。 また、useEffectフックを使用して、状態の変化に応じてイベントリスナーを適切に追加および削除する方法と、コンポーネントがアンマウントされたときにイベントリスナーをクリーンアップする方法についても学習しました。

結論

イベントハンドラーを使用すると、コンポーネントをユーザーアクションに合わせることができます。 これらは、アプリケーションに豊かなエクスペリエンスを提供し、アプリのインタラクティブな可能性を高めます。 また、ユーザーのアクションをキャプチャして応答する機能も提供します。

Reactのイベントハンドラーを使用すると、イベントコールバックをHTMLと統合しておくことができるため、アプリケーション全体で機能とデザインを共有できます。 ほとんどの場合、イベントハンドラをDOM要素に直接追加することに重点を置く必要がありますが、コンポーネントの外部でイベントをキャプチャする必要がある状況では、イベントリスナーを追加し、使用されなくなったときにクリーンアップして、メモリリークを防ぐことができます。パフォーマンスの高いアプリケーションを作成します。

Reactのチュートリアルをもっと見たい場合は、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。 JavaScriptでのイベントの詳細については、JavaScriptでのイベントの理解およびNode.jsでのイベントエミッターの使用のチュートリアルをご覧ください。