Reactでフォームの入力パーツを使用する際の注意点をいくつかメモ。
サンプルコード
まずはinputをlabelと紐づける場合です。
通常のHTMLではinputにid属性、labelにfor属性を付けて紐づけますが、Reactの場合はfor属性の代わりにhtmlForを使います。
import { useState } from 'react' function Form() { const [name, setName] = useState(''); return ( <form> <label htmlFor="name">名前</label> <input type="text" name="name" id="name" value={name} onChange={e => setName(e.target.value)} /> </form> ) } export default Form
また紐づけの際、Reactで用意されているuseIdを使用すると一意のIDを生成できるので、競合を避けることができます。
import { useState, useId } from 'react' function Form() { const [name, setName] = useState(''); const nameId = useId(); return ( <form> <label htmlFor={nameId}>名前</label> <input type="text" name="name" id={nameId} value={name} onChange={e => setName(e.target.value)} /> </form> ) } export default Form
次にtextareaですが、値の指定をvalueに対して行います。
また通常のHTMLでは終了タグをつけますが、Reactの場合は終了タグは無しでの記述になります。
import { useState, useId } from 'react' function Form() { const [message, setMessage] = useState(''); const messageId = useId(); return ( <form> <label htmlFor={messageId}>メッセージ</label> <textarea name="message" id={messageId} value={message} onChange={e => setMessage(e.target.value)} /> </form> ) } export default Form
次にラジオボタンで、ラジオボタンをループで表示する際、以下のように記述するとkeyの設定がないため警告が出ます。
ただ、フラグメントを<>~</>のように省略して記述している場合、<key=”xxx”>のようにkeyを記述するとエラーになります。
import { useState, useId } from 'react' function Form() { const genderSelect = ['男性', '女性']; const [gender, setGender] = useState(genderSelect[0]); return ( <form> {genderSelect.map(item => { const genderId = useId(); return ( <key={item}> <input type="radio" name="gender" id={genderId} value={item} checked={item === gender} onChange={e => setGender(e.target.value)} /> <label htmlFor={genderId}>{item}</label> </> ) })} </form> ) } export default Form
そのため、Fragmentをインポートして省略しない形で記述する必要があります。
import { useState, useId, Fragment } from 'react' function Form() { const genderSelect = ['男性', '女性']; const [gender, setGender] = useState(genderSelect[0]); return ( <form> {genderSelect.map(item => { const genderId = useId(); return ( <Fragment key={item}> <input type="radio" name="gender" id={genderId} value={item} checked={item === gender} onChange={e => setGender(e.target.value)} /> <label htmlFor={genderId}>{item}</label> </Fragment> ) })} </form> ) } export default Form
次にチェックボックスの例です。
ラジオボタンの場合はグループ内で選択される値は1つでしたが、チェックボックスの場合は複数選択可能なため、値管理の配列内にチェックしているかどうかを管理する値を追加します。
import { useState, useId, Fragment } from 'react' function Form() { const [contact, setContact] = useState([ { label: 'メール', checked: false }, { label: '電話', checked: false }, { label: 'FAX', checked: false } ]); const handleContactChange = (e) => { const updateContact = contact.map((item) => { const cloneItem = { ...item }; if(cloneItem.label === e.target.value) { cloneItem.checked = !cloneItem.checked; } return cloneItem; }); setContact(updateContact); } return ( <form> {contact.map(item => { const contactId = useId(); return ( <Fragment key={item.label}> <input type="checkbox" name="contact" id={contactId} value={item.label} checked={item.checked} onChange={handleContactChange} /> <label htmlFor={contactId}>{item.label}</label> </Fragment> ) })} </form> ) } export default Form
最後にselectですが、通常HTMLでは選択している項目(option)に対してselectedを付与しますが、Reactの場合は選択中の項目のvalueをselectのvalueに指定します。
import { useState, useId, Fragment } from 'react' function Form() { const areaSelect = ['北海道', '東北', '関東', '中部', '近畿', '中国', '四国', '九州']; const [area, setArea] = useState(areaSelect[0]); const areaId = useId(); return ( <form> <label htmlFor={areaId}></label> <select name="area" id={areaId} value={area} onChange={e => setArea(e.target.value)} > {areaSelect.map(item => { return ( <option key={item} value={item} >{item}</option> ) })} </select> </form> ) } export default Form
今回試した内容の一式は以下の通りです。
import { useState, useId, Fragment } from 'react' function Form() { const [name, setName] = useState(''); const nameId = useId(); const [message, setMessage] = useState(''); const messageId = useId(); const genderSelect = ['男性', '女性']; const [gender, setGender] = useState(genderSelect[0]); const [contact, setContact] = useState([ { label: 'メール', checked: false }, { label: '電話', checked: false }, { label: 'FAX', checked: false } ]); const handleContactChange = (e) => { const updateContact = contact.map((item) => { const cloneItem = { ...item }; if(cloneItem.label === e.target.value) { cloneItem.checked = !cloneItem.checked; } return cloneItem; }); setContact(updateContact); } const areaSelect = ['北海道', '東北', '関東', '中部', '近畿', '中国', '四国', '九州']; const areaId = useId(); const [area, setArea] = useState(areaSelect[0]); return ( <form> <label htmlFor={nameId}>名前</label> <input type="text" name="name" id={nameId} value={name} onChange={e => setName(e.target.value)} /> <label htmlFor={messageId}>メッセージ</label> <textarea name="message" id={messageId} value={message} onChange={e => setMessage(e.target.value)} /> {genderSelect.map(item => { const genderId = useId(); return ( <Fragment key={item}> <input type="radio" name="gender" id={genderId} value={item} checked={item === gender} onChange={e => setGender(e.target.value)} /> <label htmlFor={genderId}>{item}</label> </Fragment> ) })} {contact.map(item => { const contactId = useId(); return ( <Fragment key={item.label}> <input type="checkbox" name="contact" id={contactId} value={item.label} checked={item.checked} onChange={handleContactChange} /> <label htmlFor={contactId}>{item.label}</label> </Fragment> ) })} <label htmlFor={areaId}></label> <select name="area" id={areaId} value={area} onChange={e => setArea(e.target.value)} > {areaSelect.map(item => { return ( <option key={item} value={item} >{item}</option> ) })} </select> </form> ) } export default Form
コメントが承認されるまで時間がかかります。