第2回

Reactイベントハンドラーの型定義:onClick・onChange・onSubmit

TypeScriptでReactのイベントハンドラーを正しく型定義する方法を解説。onClick・onChange・onSubmit・onKeyDown・フォーム要素ごとの型・カスタムイベントハンドラーのprops定義まで実例付きで説明します。

·10分で読める

イベントハンドラーの型の基本

ReactのイベントはブラウザのネイティブEventではなく、Reactがラップした**合成イベント(SyntheticEvent)**です。TypeScriptでは React.MouseEventReact.ChangeEvent などの型を使います。

import { type MouseEvent, type ChangeEvent } from 'react'

const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
  console.log(e.currentTarget.textContent)
}

const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
  console.log(e.target.value)
}

onClick

// ボタンのクリック
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
  e.preventDefault()
  console.log('クリックされた')
}

<button onClick={handleClick}>クリック</button>

// divのクリック
const handleDivClick = (e: MouseEvent<HTMLDivElement>) => {}

// イベントオブジェクトが不要な場合は省略可
<button onClick={() => setCount(c => c + 1)}>+</button>

onChange:入力フォームの値を取得

const [value, setValue] = useState('')

// inputのonChange
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
  setValue(e.target.value)
}

// textareaのonChange
const handleTextChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
  setText(e.target.value)
}

// selectのonChange
const handleSelect = (e: ChangeEvent<HTMLSelectElement>) => {
  setSelected(e.target.value)
}
<input type="text" value={value} onChange={handleChange} />
<textarea value={text} onChange={handleTextChange} />
<select value={selected} onChange={handleSelect}>
  <option value="a">A</option>
  <option value="b">B</option>
</select>

チェックボックス:value ではなく checked を使う

const [checked, setChecked] = useState(false)

const handleCheckChange = (e: ChangeEvent<HTMLInputElement>) => {
  setChecked(e.target.checked)  // valueではなくchecked
}

<input type="checkbox" checked={checked} onChange={handleCheckChange} />

onSubmit:フォームの送信

import { type FormEvent } from 'react'

const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
  e.preventDefault()  // ページリロードを防ぐ

  const form = e.currentTarget
  const name = (form.elements.namedItem('name') as HTMLInputElement).value
  console.log(name)
}

<form onSubmit={handleSubmit}>
  <input name="name" type="text" />
  <button type="submit">送信</button>
</form>

onKeyDown / onKeyUp

import { type KeyboardEvent } from 'react'

const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
  if (e.key === 'Enter') {
    handleSubmit()
  }
  if (e.key === 'Escape') {
    handleClose()
  }
}

<input onKeyDown={handleKeyDown} />

イベントハンドラーをpropsとして渡す

コンポーネントにイベントハンドラーを渡すとき、型を明示します。

// ❌ 型が any になってしまう
type ButtonProps = {
  onClick: Function
}

// ✅ 正しい型定義
type ButtonProps = {
  onClick: () => void
  onChange?: (value: string) => void
  onSubmit?: (data: FormData) => void
}

const Button = ({ onClick }: ButtonProps) => (
  <button onClick={onClick}>クリック</button>
)

MouseEvent と引数を組み合わせる

クリックされた要素のIDなど、追加の引数と組み合わせる場合です。

// ❌ これは型エラー:onClickはMouseEventを受け取るので引数を増やせない
<button onClick={handleDelete(item.id)}>削除</button>

// ✅ アロー関数でラップする
<button onClick={() => handleDelete(item.id)}>削除</button>

// または、data属性を使う
const handleDelete = (e: MouseEvent<HTMLButtonElement>) => {
  const id = e.currentTarget.dataset['id']
  deleteItem(id)
}
<button onClick={handleDelete} data-id={item.id}>削除</button>

カスタムイベントの型:子→親へ値を渡す

// 子コンポーネントが親に値を返すパターン
type SearchBoxProps = {
  onSearch: (query: string) => void
}

const SearchBox = ({ onSearch }: SearchBoxProps) => {
  const [query, setQuery] = useState('')

  const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    onSearch(query)  // 親に値を渡す
  }

  return (
    <form onSubmit={handleSubmit}>
      <input value={query} onChange={e => setQuery(e.target.value)} />
      <button type="submit">検索</button>
    </form>
  )
}

// 親コンポーネント
const handleSearch = (query: string) => {
  console.log('検索:', query)
}
<SearchBox onSearch={handleSearch} />

よく使うイベント型の一覧

イベント 要素例
クリック MouseEvent<T> button, div, a
入力値変化 ChangeEvent<T> input, select, textarea
フォーム送信 FormEvent<HTMLFormElement> form
キー操作 KeyboardEvent<T> input, div
フォーカス FocusEvent<T> input, button
ドラッグ DragEvent<T> div
タッチ TouchEvent<T> div(モバイル)

まとめ

  • e.target.value を使うときは ChangeEvent<HTMLInputElement> など要素型を指定する
  • チェックボックスは e.target.checked
  • フォーム送信は必ず e.preventDefault() を呼ぶ
  • 引数を追加したいときはアロー関数でラップする
  • コンポーネントのpropsとして渡すときは () => void(value: string) => void の形で型定義する