6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

React + TypeScript: 発生したイベントを処理する

Last updated at Posted at 2023-03-22

React公式サイトのドキュメントが2023年3月16日に改訂されました(「Introducing react.dev」参照)。本稿は、基本解説の「Responding to Events」をかいつまんでまとめた記事です。ただし、コードにはTypeScriptを加えました。反面、初心者向けのJavaScriptの基礎的な説明は省いています。

なお、本シリーズ解説の他の記事については「React + TypeScript: React公式ドキュメントの基本解説『Learn React』を学ぶ」をご参照ください。

この記事で解説するのは、つぎのような項目です。

  • イベントハンドラをどう定め方るか。
  • イベント処理のロジックを親コンポーネントから子にどのように渡すか。
  • イベントの制御の仕方。

Reactのイベントハンドラは、JSXにプロパティとして加え、ハンドラ(コールバック)関数を与えます。要素へのマウスクリックやポインタの重なり、フォーム入力のフォーカスといったインタラクションなどに反応して呼び出される関数です。

イベントハンドラを加える

イベントハンドラはつぎのように加えてください。

  • コンポーネントにハンドラ(コールバック)関数を定めます。
  • 関数の参照を与えるのはJSXに書いたタグのプロパティです(「Passing Props to a Component」参照)。

つぎのコード例は、ボタン(<button>)のクリックイベント(onClick)にハンドラ関数(handleClick)を定めました。

src/App.tsx
import { MouseEventHandler } from 'react';

export default function App() {
	const handleClick: MouseEventHandler = () => {
		alert('You clicked me!');
	};
	return <button onClick={handleClick}>Click me</button>;
}

React公式サイトでは、イベントハンドラの関数とプロパティについて、つぎのような名前のつけ方を慣習として紹介しています。

イベントハンドラでプロパティを読み込む

イベントハンドラは、コンポーネントの中に定められます。つまり、コンポーネントのプロパティが参照できるということです。つぎのコードは、ボタンの子コンポーネント(AlertButton)に渡したプロパティ(message)のテキストを、クリックで開く警告ダイアログに示します。同じコンポーネントでも、異なるプロパティ値によって表示が変えられる例です。

src/App.tsx
import { AlertButton } from './AlertButton';

export default function Toolbar() {
	return (
		<div>
			<AlertButton message="Playing!">Play Movie</AlertButton>
			<AlertButton message="Uploading!">Upload Image</AlertButton>
		</div>
	);
}
src/AlertButton.tsx
import { FC, ReactNode } from 'react';

type Props = {
	message: string;
	children: ReactNode;
};
export const AlertButton: FC<Props> = ({ message, children }) => {
	return <button onClick={() => alert(message)}>{children}</button>;
};

イベントハンドラをプロパティとして渡す

子コンポーネントで起こるイベントを親のハンドラで扱いたいことがあるでしょう。そのような場合、ハンドラ関数を小コンポーネントにプロパティで渡してください。

つぎのコード例では、親コンポーネント(Toolbar)が、ハンドラ関数(handleClick)を子(AlertButton)にプロパティonClickとして渡しました。すると、子コンポーネントは、イベント(onClick)が発生したとき、受け取った関数を呼び出せばよいのです(サンプル001)。なお、親コンポーネントから子に与えるプロパティ名は(onClickでなくとも)、自由につけて差し支えありません。

src/App.tsx
import { AlertButton } from './AlertButton';

export default function Toolbar() {
	const handleClick = (message: string) => {
		alert(`Playing ${message}!`);
	};
	return (
		<div>
			<AlertButton message="Kiki's Delivery Service" onClick={handleClick}>
				Play
			</AlertButton>
		</div>
	);
}
src/AlertButton.tsx
import { FC, ReactNode } from 'react';

type Props = {
	message: string;
	children: ReactNode;
	onClick: (message: string) => void;
};
export const AlertButton: FC<Props> = ({ message, children, onClick }) => {
	return (
		<button onClick={() => onClick(message)}>
			{children} "{message}"
		</button>
	);
};

サンプル001■React + TypeScript: Responding to Events 01

イベントの制御

Reactにおけるイベントの制御は、基本的に標準JavaScriptにしたがいます。詳しくは、以下のリンク先ドキュメントをご参照ください。

Reactでイベントは原則として子コンポーネントから親に伝わります(「バブリングフェーズ」という流れです)。つぎのコード例では、onClickイベントハンドラを、親(<div>)と子(<button>)の両方の要素に加えました。このとき、ボタンをクリックすると、まず子のハンドラ、つぎに親のハンドラが呼び出されるということです。

src/App.tsx
export default function App() {
	return (
		<div
			onClick={() => {
				alert("The parent is called");
			}}
		>
			<button
				onClick={() => {
					alert("The child is called");
				}}
			>
				Button
			</button>
		</div>
	);
}

そこで、子(<button>)のハンドラ関数のはじめにEvent.stopPropagation()メソッドを加えてみましょう。すると、onClickイベントは親(<div>)に伝わらなくなり、親のハンドラは呼び出されません。

src/App.tsx
<button
	// onClick={() => {
	onClick={(event) => {
		event.stopPropagation();
		alert("The child is called");
	}}
>
	Button
</button>

イベントの流れにはもうひとつ「キャプチャリングフェーズ」があります。イベントを受け取るのは親が先で、子はあとです。この場合、イベント名の末尾にCaptureを加えます(「Capture phase events」)。イベントのバブリングとキャプチャリングについて、詳しくは「イベントへの入門」をお読みください。

つぎのコード例ではボタンクリックで、まず親(<div>)のonClickCaptureが呼び出されます(サンプル002)。そのあと実行されるのが子(<button>)のonClickイベントハンドラです。けれど、関数のはじめにEvent.stopPropagation()を呼び出しています。したがって、親のonClickにはイベントが渡りません。

src/App.tsx
export default function App() {
	return (
		<div
			onClick={() => {
				alert('The parent is called');
			}}
			onClickCapture={() => {
				alert('Capture event is triggered');
			}}
		>
		<button
			onClick={(event) => {
				event.stopPropagation();
				alert('The child is called');
			}}
		>
			Button
		</button>
		</div>
	);
}

サンプル002■React + TypeScript: Responding to Events 02

Event.preventDefault()が使われる典型は、<form>onSubmitイベントでしょう。規定のアクションとしては、ブラウザがページをすべて再読み込みしてしまいます。Event.preventDefault()の呼び出しにより、読み込む必要のあるコンポーネントや状態をReactが決められるのです。

イベントハンドラと副作用について触れておきましょう。イベントハンドラは、入力に応じた値の変更やボタンクリックによるデータの書き替えなど、副作用を実行するのに適しています。レンダリング関数とは異なり、ハンドラ関数は純粋でなくてよいからです(「Can event handlers have side effects?」参照)。

まとめ

この記事では、つぎのような項目についてご説明しました。

  • イベントハンドラを定めるのはコンポーネントのプロパティです。
  • イベントハンドラはコンポーネントが受け取ったプロパティを参照できます。
  • 親コンポーネントに定めたハンドラ関数を子にプロパティとして渡しても構いません。
  • イベントの制御は原則として標準JavaScriptにしたがいます。
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?