Next.jsでのフォームまわりの処理をメモしておく
こんにちは。
React(Next.js)でのフォーム処理は一般的なタスクですね。
フォームによっては「あれ?どうするんだっけ?」ということがよくあるので備忘録としてメモしておきます。
多くのサンプルはコンポーネント化されていないことが多いですが、今回は各パーツをしっかりとコンポーネント化して進めてみます。
1.text
'use client'
import React from 'react'
//---------------------------------------------
// props
//---------------------------------------------
export type Props = {
type: 'text' | 'password' | 'number' | 'email' | 'tel'
name: string
value: string
placeholder?: string
disabled?: boolean
min?: number
max?: number
onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void
}
//---------------------------------------------
// component
//---------------------------------------------
export const InputText = ({ type, name, value, placeholder, disabled, min, max, onChange }: Props) => {
return (
<input
type={type}
name={name}
value={value}
placeholder={placeholder}
disabled={disabled}
minLength={min}
maxLength={max}
onChange={onChange}
/>
)
}
2.textarea
'use client'
import React from 'react'
//---------------------------------------------
// props
//---------------------------------------------
export type Props = {
name: string
value: string
placeholder?: string
disabled?: boolean
min?: number
max?: number
rows?: number
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void
}
//---------------------------------------------
// component
//---------------------------------------------
export const InputTextarea = ({ name, value, placeholder, disabled, min, max, rows = 8, onChange }: Props) => {
return (
<textarea
name={name}
value={value}
placeholder={placeholder}
disabled={disabled}
minLength={min}
maxLength={max}
rows={rows}
onChange={onChange}
/>
)
}
3.Radio
'use client'
import React from 'react'
//---------------------------------------------
// props
//---------------------------------------------
export type Props = {
name: string
value: string
id: string
checked: boolean
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
}
//---------------------------------------------
// component
//---------------------------------------------
export const InputRadio = ({ name, id, value, checked, onChange }: Props) => {
return (
<label htmlFor={id}>{value}</label>
<input type={'radio'} name={name} id={id} value={value} checked={checked} onChange={onChange} />
)
}
4.Check
'use client'
import React from 'react'
//---------------------------------------------
// props
//---------------------------------------------
export type Props = {
name: string
value: string
id: string
checked: boolean
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void
}
//---------------------------------------------
// component
//---------------------------------------------
export const InputCheck = ({ name, id, value, checked, onChange }: Props) => {
return (
<label htmlFor={id}>{value}</label>
<input type={'checkbox'} id={id} name={name} value={value} checked={checked} onChange={onChange} />
)
}
5.File
'use client'
import React from 'react'
//---------------------------------------------
// props
//---------------------------------------------
export type Props = {
name: string
id: string
selectedImage: File | null
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
}
//---------------------------------------------
// component
//---------------------------------------------
export const InputImage = ({ name, id, selectedImage, onChange }: Props) => {
return (
<>
<Input type={'file'} accept={'image/*'} name={name} id={id} onChange={onChange} />
<Label htmlFor={id}>画像アップ</Label>
{selectedImage && (
<div>
<p>Selected Image:</p>
<img src={URL.createObjectURL(selectedImage)} alt="Selected" width={200} />
</div>
)}
</>
)
}
6.各パーツをつかうとき
'use client'
import React, { ChangeEvent, useState } from 'react'
import { InputText } from '@/components/InputText/InputText'
import { InputTextarea } from '@/components/InputTextarea/InputTextarea'
import { InputRadio } from '@/components/InputRadio/InputRadio'
import { InputCheck } from '@/components/InputCheck/InputCheck'
import { InputImage } from '@/components/InputImage/InputImage'
//---------------------------------------------
// component
//---------------------------------------------
export const Form = ({ datas }: Props) => {
const [data, setData] = useState<{
text: string;
textarea: string;
check: { name: string; checked: boolean }[];
radio: { name: string; checked: boolean }[];
image: File | null;
}>({
text: '',
textarea: '',
check: [],
radio: [],
image: null
})
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
// 各inputのname / value / checked(true or false)が取得できる
const target = e.target as HTMLInputElement;
const { name, value, checked } = target;
// 各input=nameで処理を分岐
if(name === 'text') {
// ...
}
if(name === 'textarea') {
// ...
}
if(name === 'radio') {
// ...
}
if(name === 'checkbox') {
// ...
}
if(name === 'image') {
try {
const file = event.target.files && event.target.files[0];
if (file) {
// ファイル入力されたときの処理をする
}
} catch (error) {
// エラーのときの処理をする
}
}
}
return (
<form>
<InputText
type={'text'}
name={'text'}
value={data.text}
placeholder={'文章を入力する'}
onChange={handleChange}
/>
<InputTextarea
name={'textarea'}
value={data.textarea}
placeholder={'文章を入力する'}
onChange={handleChange}
/>
{data.radio.map((radio, index) => {
return (
<InputRadio
name={'radio'}
id={`${radio}_${index}`}
value={radio.name}
checked={radio.checked}
onChange={handleChange}
/>
)
})}
{data.check.map((check, index) => {
return (
<InputCheck
name={'check'}
id={`${check.name}_${index}`}
value={check.name}
checked={check.checked}
onChange={handleChange}
/>
)
})}
<InputImage
name={'images'}
id={'images'}
selectedImage={data.image}
onChange={handleImageChange}
/>
<button type={'submit'}>送信</button>
</form>
)
}
よく忘れるので、今回ここにメモしておきます。