突然ですがこういうフォームっていいですよね!
- 入力が終わると、エラーかどうかを判定してエラー文を表示してくれる
- 正しい入力に戻ると、エラー文が消える
gifにするとこういう感じ!
シンプルですが、「どういう風に書けばこうなるか」は案外知らないと作れないと思います
今回はこういうフォームの作り方をさくっとまとめます
要点をまとめると
-
onChange
で、「入力値」をuseState
へ保存する -
onBlur
で、「入力値を判定したエラー文」をuseState
へ保存する
これがわかればできます!
実装
では書いていきます
まずHTMLですが、「ラベル(はなくてもいいけど)」「入力欄」「エラー文を出す場所」があればいいです!
<div>
<label>unique name</label>
<input
type="text"
name="name"
placeholder="Enter your name"
/>
<p>error</p>
</div>
次にここへuseStateのロジックを混ぜていきましょう!
const [name, setName] = useState('')
const [nameError, setNameError] = useState('')
return (
<div>
<label>unique name</label>
<input
type="text"
name="name"
placeholder="Enter your name"
value={name}
onChange={(e) => {
setName(e.target.value)
}}
/>
{nameError && <p>{nameError}</p>}
</div>
)
一気に増えましたが、大事なところだけ解説します!
まずuseStateで、必要なパラメーター「入力値」「入力値エラー」を作ります
const [name, setName] = useState('')
const [nameError, setNameError] = useState('')
inputタグのvalueとonChange属性にnameとsetNameを渡すことで、useStateの値と、入力フォームの中身を一致させることができます
<input
type="text"
name="name"
placeholder="Enter your name"
value={name}
onChange={(e) => {
setName(e.target.value)
}}
/>
次にエラー文。ちょっと慣れない書き方かもしれませんが、こういう風に書くと「nameErrorがfalseじゃない時」つまり、エラー文が存在する時だけ、うしろのpタグの中身を出してくれます
jsxならではの書き方ですね
{nameError && <p>{nameError}</p>}
最後の仕上げです!
onBlur属性の関数を定義して、入力フォームから離れた時に、入力値を判定して、エラー文を表示できるようにします
const [name, setName] = useState('')
const [nameError, setNameError] = useState('')
const handleBlur = (e) => {
const name = e.target.value
if (!name) {
setNameError('required')
} else if (name.length < 5) {
setNameError('should longer than 5')
} else {
setNameError()
}
}
return (
<div>
<label>unique name</label>
<input
type="text"
name="name"
placeholder="Enter your name"
value={name}
onChange={(e) => {
setName(e.target.value)
}}
onBlur={handleBlur}
/>
{nameError && <p>{nameError}</p>}
</div>
)
onBlurは他のところをクリックした時など、入力を中断した時だけ走ってくれるので、入力中には動作しません
また嬉しい点として、入力途中でボタンをクリックしても、ボタンのクリックイベントより先にonBlurが走ってくれるので、うっかり変なデータを送信することもありません!
ボタンも一緒に作る場合は、disabledを設定しておくと良いでしょう。(nameErrorが存在する時にdisabledがtrueになるようにする)
const [name, setName] = useState('')
const [nameError, setNameError] = useState('')
const handleBlur = (e) => {
const name = e.target.value
if (!name) {
setNameError('required')
} else if (name.length < 5) {
setNameError('should longer than 5')
} else {
setNameError()
}
}
return (
<div>
<label>unique name</label>
<input
type="text"
name="name"
placeholder="Enter your name"
value={name}
onChange={(e) => {
setName(e.target.value)
}}
onBlur={handleBlur}
/>
{nameError && <p>{nameError}</p>}
</div>
<div>
<button disabled={nameError}>Save</button>
</div>
)
おまけ:入力値が空の時のe.target.value
...へは「空文字」が渡ってきます。
というより基本的にはe.target.value
は、数字を書いたとしても、基本全て文字列で渡ってきます(e.target.valueAsNumber
というのもありますが、それはまた別の話ということで...)
つまり「文字列型」なので、今回のようなnameと行った文字列の入力値なら良いのですが、数字を扱いたい場合は都合が悪いです
なので、そういう場合はonChangeで仲介してあげましょう!
入力値が数字の場合の「空」をどう表現するかは人によると思うのですが、私はundefined
を使っています!
// 年齢のフォーム
<input
type="number"
name="age"
placeholder="Enter your age"
value={age}
onChange={(e) => {
if (e.target.value === '') {
setAge(undefined)
} else {
// 入力値を数値へ変換!
setAge(Number(e.target.value))
}
}}
/>
終わりに
今回はuseStateとinputのイベントハンドラを使って、エラー文を綺麗に出すフォームを作ってみました!
CSSやアニメーションをもっと付ければ、より洗練された感じになるので、是非やってみてください!