【React】 フォームの作成をして、typeに応じた値の管理をする

Reactでは、フォームを表示する際にデフォルトで選択しておいたり、値を簡単に変数に入れるといったことが可能です。チェックボックスやmultipleなセレクトといった、複数項目を管理するものも見ていきましょう。

変数とフォームの値を同期する

まずはシンプルな1行テキストや数値の場合を見てみます。

inputに入れる値はvalue属性に、ユーザが入力した値を格納するには、onChangeなどを使用して更新します。

import { useState } from "react";

export default function MyForm() {
    const [name, setName] = useState("abc");

    const handleChangeName = (e) => {
        setName(e.target.value);
    }

    const handleSubmit = (e) => {
        // ...
    }

    return (
        <>
        <p>入力中の値: {name}</p>
        <form onSubmit={handleSubmit} method="get">
            <input type="text" name="name" value={name} onChange={handleChangeName} />
        </form>
        </>
    )
}

この例では、inputの値にはnameを、値の更新にはhandleChangeName関数を使用しています。onChangeなので、入力値が更新される度にhandleChangeNameが呼ばれます。

要素のイベントで入力されている値を取得するには、e.target.valueのようにします。targetは普通のイベントにおいても、イベントが発生した要素のDOMを取得するものでした。

フォームの送信

送信時に値を取得する際も、通常のイベントと同じように処理します。

画面遷移を防止したい場合、e.preventDefault();する必要があります。

const handleSubmit = (e) => {
    // 送信後の画面遷移を防止
    e.preventDefault();


    // form内の値であれば、name属性の値を指定して値を取得できる
    const name = e.target.name.value;

    // ...
}

種類に応じた値

inputの種類によって、値の入り方が違います。それぞれを見ていきましょう。

text、number、他文字列系のもの

typeがtextnumberでは、普通の文字列を取り扱います。numberでも、取得する際は文字列です。

他にも、emailtelなども普通の文字列です。

const handleChangeName = (e) => {
    // textでもnumberでも普通の文字列
    setName(e.target.value);
}
<input type="text" value={name} onChange={handleChangeName} />

date

日付型の場合、yyyy-mm-ddの形式を利用します。値自体は文字列です。

const [value, setValue] = useState("2022-04-01");

const handleChangeValue = (e) => {
    setName(e.target.value);
}
<input type="date" value={value} onChange={handleChangeValue} />

textarea

textareaでも、扱い方はinputと同じです。もし文字列を改行したい場合は\nを入れます。

const [text, setText] = useState("hoge\nfuga\nhige");

const handleChangeText = (e) => {
    setText(e.target.value);
}
<textarea value={text} onChange={handleChangeText} cols="30" rows="10"></textarea>

ラジオボタン

ラジオボタンでは、一連の同じnameのうち1つを選択します。この時、チェックを入れたものを変更したときの挙動はonChangeを利用して他と同様に設定します。

チェックを入れたときの値はvalue属性に入れ、最初からチェックが入っているかどうかの設定にはchecked属性にbooleanで入れます。

// valueにはチェックが入っている項目のvalue属性の値が入る
const [value, setValue] = useState("bar");

const handleChangeValue = (e) => {
    setValue(e.target.value);
}
foo <input type="radio" name="radio1" value="foo" onChange={handleChangeValue} checked={value === "foo"} />
bar <input type="radio" name="radio1" value="bar" onChange={handleChangeValue} checked={value === "bar"} />
baz <input type="radio" name="radio1" value="baz" onChange={handleChangeValue} checked={value === "baz"} />

チェックボックス

チェックボックスでは、チェックが入っているかどうかの状態の管理は自前で実装する必要があります。キーにvalue属性の値を、値にチェックが入っているかのbooleanを持たせて管理すると良いです。

もちろん、それ以外の実装でも大丈夫です。

const [values, setValues] = useState({});

const handleChangeValue = (e) => {
    setValues({
        ...values,
        [e.target.value]: e.target.checked
    });
}
foo:<input type="checkbox" value="foo" onChange={handleChangeValue} checked={values["foo"] ?? false} />
bar:<input type="checkbox" value="bar" onChange={handleChangeValue} checked={values["bar"] ?? false} />
baz:<input type="checkbox" value="baz" onChange={handleChangeValue} checked={values["baz"] ?? false} />

checked={values["baz"] ?? false}のようにしているのは、undefinedfalseの扱いが異なるためです。

undefinedの場合は、その属性を制御対象とはしないという扱いにします。これがtrueに変わると、制御対象としていないはずのものが制御対象となるため、コードが間違っているのではないかと解釈され、警告が表示されます。

本当にそういった場合に気がつけるように、undefinednullではなくfalseが入るようにしておく必要があります。

select

selectは複数項目から1つを選ぶものでした。selectタグ側のvalueに、選択したいoptionsvalue属性の値を設定します。

const [value, setValue] = useState("bar");

const handleChangeValue = (e) => {
    setValue(e.target.value);
}
<select value={value} onChange={handleChangeValue}>
    <option value="foo">foo</option>
    <option value="bar">bar</option>
    <option value="baz">baz</option>
</select>

multiple

selectでは、multiple属性を付けると複数選択が可能になります。multipleの場合、配列で管理します。

ここで、選択されている項目を取得してセットする操作は自前で実装する必要があります。

const [value, setValue] = useState(["bar", "baz"]);

const handleChangeValue = (e) => {
    // HTMLCollectionでは操作が制限されるので、配列に変換
    const selections = Array.prototype.slice.call(e.target.selectedOptions);

    // 選択されている項目の`value`を取得
    setValue(selections.map(v => v.value));
}
<select value={value} onChange={handleChangeValue} multiple>
    <option value="foo">foo</option>
    <option value="bar">bar</option>
    <option value="baz">baz</option>
</select>

file

fileは、Reactでは制御できない種類になります。できることといえば、DOMノードへの参照を作成することくらいです。

const [value, setValue] = useState(null);

const handleChangeValue = (e) => {
    setValue(e.target);

    // ファイルパスが取得できる (セキュリティのため、道中のパスは自動で隠される)
    console.log(e.target.value);
}
<input type="file" onChange={handleChangeValue} />

まとめ

各種inputの形式に応じた値の更新や格納方法はおおよそ似通っています。ラジオボタン、チェックボックス、ファイルの3つをどう扱えば良いかを把握すれば、殆どのフォームを作ることができるようになります。

react form thumb

役に立ったらシェアしよう!