React

フォーム管理

React

制御・非制御コンポーネント・バリデーション

制御コンポーネント

Reactが入力値を管理するパターン

ControlledForm.tsx tsx
import { useState, type FormEvent, type ChangeEvent } from 'react';

interface FormData {
    name:     string;
    email:    string;
    password: string;
    role:     'user' | 'admin';
    agree:    boolean;
}

function SignupForm() {
    const [form, setForm]   = useState<FormData>({
        name: '', email: '', password: '', role: 'user', agree: false
    });
    const [errors, setErrors] = useState<Partial<FormData>>({});

    // 汎用ハンドラー
    const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
        const { name, value, type } = e.target;
        setForm(prev => ({
            ...prev,
            [name]: type === 'checkbox' ? (e.target as HTMLInputElement).checked : value,
        }));
    };

    const handleSubmit = (e: FormEvent) => {
        e.preventDefault();
        const errs: Partial<FormData> = {};
        if (!form.name)  errs.name  = '名前は必須です';
        if (!form.email) errs.email = 'メールは必須です';
        if (Object.keys(errs).length) { setErrors(errs); return; }
        // submit処理...
    };

    return (
        <form onSubmit={handleSubmit}>
            <input name="name"  value={form.name}  onChange={handleChange} />
            {errors.name && <p className="text-red-500">{errors.name}</p>}
            <input name="email" value={form.email} onChange={handleChange} type="email" />
            <input name="agree" checked={form.agree} onChange={handleChange} type="checkbox" />
            <button type="submit">登録</button>
        </form>
    );
}

非制御コンポーネント / useFormStatus

ref・FormData・Server Actions

uncontrolled.tsx tsx
import { useRef } from 'react';

// 非制御コンポーネント(シンプルなフォームに)
function SimpleForm() {
    const nameRef  = useRef<HTMLInputElement>(null);
    const emailRef = useRef<HTMLInputElement>(null);

    const handleSubmit = (e: FormEvent) => {
        e.preventDefault();
        const name  = nameRef.current?.value;
        const email = emailRef.current?.value;
        // 処理...
    };

    return (
        <form onSubmit={handleSubmit}>
            <input ref={nameRef}  defaultValue="" />
            <input ref={emailRef} defaultValue="" type="email" />
            <button type="submit">送信</button>
        </form>
    );
}

// FormData API(ネイティブフォームデータ取得)
function NativeForm() {
    const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
        e.preventDefault();
        const data = new FormData(e.currentTarget);
        const name  = data.get('name') as string;
        const email = data.get('email') as string;
        // 処理...
    };

    return (
        <form onSubmit={handleSubmit}>
            <input name="name" />
            <input name="email" type="email" />
            <button type="submit">送信</button>
        </form>
    );
}