Next.js と TypeScript で学ぶイベント処理「e.target.value」

TwitterFacebookHatena

TL;DR

このページでは、Next.js と TypeScript を使って、フォームの入力値(e.target.value)の取り扱いについて詳しく解説します。フォームはウェブ開発において欠かせない要素です。ユーザーの入力を取得して、それをアプリケーションのどこかで使用する方法を学びます。

開発環境 バージョン
Next.js 13.4.4
TypeScript 5.0.4
Emotion 11.11.0
React 18.2.0

e.target.value とは?

e.target.value は、イベントが起きた要素の value 属性の値を取得することができます。

e はイベントを表し、target はイベントが発生した要素(ここでは入力要素)を指し、value はその要素の現在の値を指します。イベントオブジェクトの名前は任意なので、e 以外にも eventev など、何でもよいです。

なんだか難しいですよね?e.target.value を理解するために、まず HTML のフォームと JavaScript の世界を少しご紹介します。

みんながよく見かけるウェブサイトには、様々な情報を入力するためのフォームがあります。例えば、ユーザー名を入力したり、パスワードを設定したり、お問い合わせのメッセージを書いたりしますよね。これらはすべて、ウェブページ上の「フォーム」という部分で行われます。

JavaScript という言語は、ウェブページ上で色々な動きを作るために使われます。例えば、ボタンをクリックした時に何かしらのアクションを起こしたり、フォームに入力した情報を収集したりします。

そして、ここでe.target.valueの出番です。これは JavaScript の中で、ユーザーがフォームに入力した情報を取得するためのコードの一部です。さて、分解して解説します。

「e」はイベント(Event)を表し、何かしらのアクション(クリックや入力など)が起こったことを示しています。

「target」はそのイベントが起こった具体的な場所や要素(この場合はフォームの入力欄)を指しています。

「value」はその場所での具体的な値、つまりユーザーが入力した情報を表します。

つまり、e.target.valueは「ユーザーが何かアクションを起こした場所で(target)、そのアクションによって生まれた具体的な情報(value)を取得する」という意味になります。

このようにe.target.valueは、ウェブサイト上でユーザーが入力した情報を収集し、その情報を使って色々なこと(ユーザーへのメッセージの表示、データベースへの情報の保存など)を行うためにとても重要な役割を果たしているんですね。

「3 文字以上入力したら、コンソールに「3 文字以上はダメですよ」と表示する」コードを書いてみましょう。

import { useState, useCallback } from 'react'

const MyComponent = () => {
  const [text, setText] = useState('') // text の初期値を空文字に設定

  // 3文字以上入力したら、コンソールに「3文字以上はダメですよ」と表示する
  const handleChange = useCallback((e) => {
    if (e.target.value.length > 3) {
      console.log('3文字以上はダメですよ')
      return
    }
    setText(e.target.value)
  }, [])

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

export default MyComponent

このコードは、入力文字数の制限が 3 文字に変更され、5 文字以上の入力があるとコンソールにメッセージが表示されます。

次は、ボタンをクリックするとそのボタンのvalue属性の値をコンソールに出力するシンプルなコンポーネントです。

type ButtonProps = {
  value: string
}

const ButtonComponent = ({ value }: ButtonProps) => {
  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    console.log(event.currentTarget.value)
  }

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

e.target.value ではなく event.currentTarget.value となっていますね。これは、eevent は同じものを指しています。

イベントオブジェクトの名前は任意で、一般的に eventeev などがよく使われます。したがって、e.target.value も一般的によく見かける形式です。これは「イベントオブジェクトの名前(ここでは e)」+「.target.value」で理解することができます。

event.targetevent.currentTarget の違い

一方、 event.targetevent.currentTarget の違いについては、具体的な要素を指す対象が異なります。どちらもイベントが発生した際の要素に関する情報を保持していますが、event.target は実際にイベントが発生した要素(例:ボタンの中のテキストなど)を指し、event.currentTarget はイベントリスナーがアタッチされた要素(例:ボタン自体)を指します。

なお、上記のコードサンプルでは、イベントリスナーがボタン要素に直接アタッチされているため、event.target.valueevent.currentTarget.value は同じ結果を返すことになります。ただし、複雑な DOM 構造やイベント伝播(バブリングやキャプチャリング)を考慮する場合には、この 2 つのプロパティが異なる結果を返すこともありますので、適切なプロパティを選んで使用することが重要です。

ここでは、ButtonComponentというコンポーネントを作っています。このコンポーネントはvalueという文字列を受け取り、それをボタンのvalue属性として設定します。

ボタンがクリックされると、handleClick関数が実行されます。この関数は、イベントオブジェクト(ここではevent)を引数に取り、そのイベントオブジェクトのcurrentTarget.value(ここではボタンのvalue属性の値)をコンソールに出力します。

なお、ボタンのイベントハンドラーで event.target.value ではなく event.currentTarget.value を使っている理由は、イベントの伝播の中でイベント発生元が変わる可能性(たとえば子要素がイベントを発生させた場合など)があるためです。currentTarget はイベントハンドラーがアタッチされている要素(ここではボタン自体)を常に指すため、このコードでは event.currentTarget.value が安全です。

e の中には何が入っているのか?

イベントハンドラの引数 e(または event と名付けられます)は、発生したイベントに関する情報を保持したイベントオブジェクトです。このオブジェクトには、イベントに関する様々な情報やメソッドが含まれています。

具体的にどのような情報が含まれるかは、イベントの種類(クリックイベント、キーボードイベント、フォームの送信イベントなど)によります。しかし、一般的に以下のようなプロパティやメソッドが含まれます。

target: イベントが発生した DOM 要素を参照します。これを使うと、たとえば <input> 要素の値を取得したり、クリックされたボタンのテキストを読み取ったりすることができます。

currentTarget: イベントリスナーがアタッチされている要素を参照します。イベントバブリングを通じて親要素でイベントをキャッチした場合でも、target はオリジナルの発火源を指しますが、currentTarget はイベントリスナーが実際に設定されている要素(つまり、イベントをキャッチした要素)を指します。

type: 発生したイベントの種類(例えば "click"、"keydown" 等)を文字列として返します。

preventDefault(): このメソッドを呼び出すと、イベントのデフォルトの動作(例えばリンクのクリックによるページ遷移や、フォームの送信によるページリロード)を防げます。

stopPropagation(): このメソッドを呼び出すと、イベントの伝播を停止できます。これにより、イベントが親要素にバブリングしたり、子要素にキャプチャリングされるのを防げます。

これらはイベントオブジェクトの一部であり、具体的なイベントの種類により、さらに多くの情報が提供されることがあります。

それでは、以下に、select 要素の選択項目の値を event.target.value で取得する簡単な例を示します。

import { useState } from 'react'

type Props = {
  options: string[]
}

const SelectComponent = ({ options }: Props) => {
  const [selectedOption, setSelectedOption] = useState('')

  const handleChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
    setSelectedOption(event.target.value)
  }

  return (
    <div>
      <select value={selectedOption} onChange={handleChange}>
        {options.map((option, index) => (
          <option key={index} value={option}>
            {option}
          </option>
        ))}
      </select>
      <p>選択された項目: {selectedOption}</p>
    </div>
  )
}

ここでは、SelectComponentというコンポーネントを作っています。これは、複数の選択肢から一つを選ぶという、select 要素を含むコンポーネントです。

このコンポーネントはoptionsという配列を受け取ります。そして、その配列の各要素をselect 要素の選択肢として表示します。

ユーザーが選択肢の中から一つ選択すると、その選択された項目の値(ここではevent.target.value)がhandleChange 関数によって取得され、その値がselectedOption というステートにセットされます。

最後に、選択された項目は画面上に表示されます。これにより、ユーザーがどの項目を選択したかが一目でわかるようになります。

Next.js で、フォーム入力値を扱う

次のコードスニペットは、Next.js と TypeScript を使った簡単なフォームの例です。

ファイル名: components/MyForm.tsx

import { ChangeEvent, useState } from 'react'

type MyFormProps = {
  onSubmit: (formValue: string) => void
}

const MyForm = ({ onSubmit }: MyFormProps) => {
  const [inputValue, setInputValue] = useState('')

  const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    setInputValue(e.target.value)
  }

  const handleSubmit = () => {
    onSubmit(inputValue)
  }

  return (
    <div>
      <input type="text" value={inputValue} onChange={handleInputChange} />
      <button onClick={handleSubmit}>Submit</button>
    </div>
  )
}

export default MyForm

上述したコードでは、まず useState を使って inputValue の状態を管理します。続いて、handleInputChange 関数では、入力値(e.target.value)が変更されるたびに setInputValue を呼び出して状態を更新します。

フォーム入力値を取り扱う高度な例

import { ChangeEvent, FormEvent, useState } from 'react'

type AdvancedFormProps = {
  onSubmit: (formValues: { name: string; email: string }) => void
}

const AdvancedForm = ({ onSubmit }: AdvancedFormProps) => {
  const [formValues, setFormValues] = useState({ name: '', email: '' })

  const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    setFormValues({
      ...formValues,
      [e.target.name]: e.target.value,
    })
  }

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault()
    onSubmit(formValues)
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" name="name" value={formValues.name} onChange={handleInputChange} />
      <input type="email" name="email" value={formValues.email} onChange={handleInputChange} />
      <button type="submit">Submit</button>
    </form>
  )
}

export default AdvancedForm

この例では、複数の入力要素を一つの状態オブジェクトで管理します。各入力要素には name 属性が設定されており、これが状態の更新に使用されます。handleInputChange 関数では、e.target.name をキーとして使用し、それに対応する e.target.value を値として設定します。

Emotion で実装

Emotion を用いたスタイリングの例も見てみましょう。ここでは AdvancedForm コンポーネントにスタイルを追加します。

/** @jsxImportSource @emotion/react */
import { css } from '@emotion/react'
import { ChangeEvent, FormEvent, useState } from 'react'

const formStyle = css`
  display: flex;
  flex-direction: column;
  gap: 1em;
`

const inputStyle = css`
  padding: 0.5em;
  font-size: 1em;
  border: 1px solid #ddd;
  border-radius: 4px;
`

type StyledFormProps = {
  onSubmit: (formValues: { name: string; email: string }) => void
}

const StyledForm = ({ onSubmit }: StyledFormProps) => {
  const [formValues, setFormValues] = useState({ name: '', email: '' })

  const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    setFormValues({
      ...formValues,
      [e.target.name]: e.target.value,
    })
  }

  const handleSubmit = (e: FormEvent) => {
    e.preventDefault()
    onSubmit(formValues)
  }

  return (
    <form css={formStyle} onSubmit={handleSubmit}>
      <input type="text" name="name" value={formValues.name} onChange={handleInputChange} css={inputStyle} />
      <input type="email" name="email" value={formValues.email} onChange={handleInputChange} css={inputStyle} />
      <button type="submit">Submit</button>
    </form>
  )
}

export default StyledForm

上述したコードでは、css 関数を使ってスタイルを定義し、それをコンポーネントに適用します。css

関数はスタイルを文字列として書き、その結果をコンポーネントの css prop に渡すことでスタイルが適用されます。

以上が、Next.js と TypeScript を用いた e.target.value の使用例とそのスタイリングになります。

Next.js と TypeScript で学ぶイベント処理「e.target.value」