はじめに
入力欄に入力された値を使ってあれやこれやしたいこと、ありますよね?
今回は、前回作成した入力フォームを改修して、入力値を state でインタラクティブに扱うようにしてみました。
入力値を state で扱うことは React をやっていれば避けては通れないので、必ず習得しておきましょう!
実物はこちら
リポジトリはこちら
概要
この記事を読むことで、以下のような画面を作れるようになります。
以下の仕様を想定して作っています。
- 「名前」「かな」の二つの入力欄がある
- 両方とも必須で、30字以内という制限がある
- バリデーションは、入力欄に値が入力されたとき、SUBMITボタンが押されたときに行われる
- エラーメッセージは、それぞれの入力欄の下に赤字で表示する
- エラーのある入力欄は赤く表示する
実装
先にコード全容です。
'use client'
import {Box, Button, Typography} from "@mui/material";
import {ChangeEvent, FormEvent, useState} from "react";
import './style.css'
import {ValidateError} from "@/types/validate-scratch";
export default function ValidateState() {
const [fullName, setFullName] = useState<string>('')
const [fullNameKana, setFullNameKana] = useState<string>('')
const [errors, setErrors] = useState<ValidateError[]>([])
const onSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault()
const errors = validateForm()
setErrors(errors)
if (errors.length) {
return
}
alert('No validate errors!')
}
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
switch (event.target.name) {
case 'fullName': {
setFullName(event.target.value)
break
}
case 'fullNameKana': {
setFullNameKana(event.target.value)
break
}
default:
}
const errorsWithoutTarget = errors.filter(error => error.key !== event.target.name)
const newErrors = errorsWithoutTarget.concat(validate(event.target.name, event.target.value))
setErrors(newErrors)
}
const validateForm = (): ValidateError[] => {
return [
...validate('fullName', fullName),
...validate('fullNameKana', fullNameKana)
]
}
const validate = (propertyName: string, value: string): ValidateError[] => {
const errors: ValidateError[] = [];
if (value === '') {
errors.push({
key: propertyName,
message: '入力してください'
})
}
if ((value as string).length > 30) {
errors.push({
key: propertyName,
message: '30字以下で入力してください'
})
}
return errors
}
const getErrorByPropertyName = (propertyName: string) => {
return errors.filter(error => error.key === propertyName)
}
const getErrorMessageByPropertyName = (propertyName: string) => {
return getErrorByPropertyName(propertyName).map((error, index) => <span key={`${index}-${error.key}`}>{error.message}</span>)
}
const isError = (propertyName: string) => {
return getErrorByPropertyName(propertyName).length > 0
}
return (
<main>
<Box mt={2} ml={2}>
<form onSubmit={onSubmit}>
<Box mb={2}>
<Box mb={1}>
<span>名前</span>
</Box>
<Box>
<input type="text" name="fullName" className={isError('fullName') ? 'error' : ''} onChange={handleChange} />
</Box>
<Box height="1rem" sx={{color: 'red'}}>
{
getErrorMessageByPropertyName('fullName')
}
</Box>
</Box>
<Box mb={2}>
<Box mb={1}>
<span>かな</span>
</Box>
<Box>
<input type="text" name="fullNameKana" className={isError('fullNameKana') ? 'error' : ''} onChange={handleChange} />
</Box>
<Box height="1rem" sx={{color: 'red'}}>
{
getErrorMessageByPropertyName('fullNameKana')
}
</Box>
</Box>
<Box>
<Button type="submit" variant="contained" disabled={errors.length > 0}>SUBMIT</Button>
</Box>
</form>
</Box>
<Box mt={2} ml={2}>
<Box>
<Typography variant="body2">
{fullNameKana !== '' && `${fullNameKana}さん、こんにちは。` }
</Typography>
</Box>
<Box>
<Typography variant="h4">
{fullName !== '' && `${fullName}さん、こんにちは。` }
</Typography>
</Box>
</Box>
</main>
)
}
各部分の解説
入力値のstateの宣言
「名前」「かな」に対応する state を宣言します。
特に変わったところはなく、一般的な useState
の使い方ですね。
const [fullName, setFullName] = useState<string>('')
const [fullNameKana, setFullNameKana] = useState<string>('')
入力値の変更を state に反映する
前回は onBlur
で処理していた箇所を、onChange
に変更して、入力値がすぐに state に反映されるようにしました。
バリデーションも忘れずに行いましょう。
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
switch (event.target.name) {
case 'fullName': {
setFullName(event.target.value)
break
}
case 'fullNameKana': {
setFullNameKana(event.target.value)
break
}
default:
}
const errorsWithoutTarget = errors.filter(error => error.key !== event.target.name)
const newErrors = errorsWithoutTarget.concat(validate(event.target.name, event.target.value))
setErrors(newErrors)
}
入力値をインタラクティブに表示する
state を使うことで、入力値の変更を即座に画面に反映することができるようになります。
<Box mt={2} ml={2}>
<Box>
<Typography variant="body2">
{fullNameKana !== '' && `${fullNameKana}さん、こんにちは。` }
</Typography>
</Box>
<Box>
<Typography variant="h4">
{fullName !== '' && `${fullName}さん、こんにちは。` }
</Typography>
</Box>
</Box>
</main>
今回は、ただ画面に入力値を表示するだけでしたが、加工して表示したり条件判定に使ったり、いろんなことができます。
おわりに
いかがだったでしょうか?
今回は入力フォームの値を state でインタラクティブに扱ってみました。
state は React で開発する際の醍醐味なので、ぜひマスターしてくださいね。
ハコラトリは、Reactを習得したい駆け出しエンジニアを応援しています。
Reactレシピでは、これからもReactで色々なUIを作って紹介していきますので、「こんなUIを作ってみてほしい」「こんな場合はどうすれば?」といったアイディアを募集中です!
コメントお待ちしております。
コメント