Form Lanjutan — React

Form login 2 field cukup pakai useState + validasi sederhana. Tapi form registrasi 10 field dengan validasi kompleks (email format, password 8+ karakter dengan

Form login 2 field cukup pakai useState + validasi sederhana. Tapi form registrasi 10 field dengan validasi kompleks (email format, password 8+ karakter dengan simbol, pengulangan password match, captcha, conditional fields)? Pakai useState manual jadi mimpi buruk. Pas form mulai gemuk, validasi + state management jadi 80% kerjaan, 20% UI. Solusi: pisahin validasi rules dari UI, pakai library seperti React Hook Form + Zod — validasi schema-based, error handling otomatis, performa bagus.

💡 Stack modern 2026. react-hook-form (state + validation) + zod (schema) + @hookform/resolvers/zod (binding). Combine ini dipake di hampir semua project React production tahun ini. Project hypersheets-frontend juga pakai pattern ini. Belajarnya 2 jam, hemat ribuan jam debugging form bug.

Validasi Manual

function RegistrationForm() {
  const [form, setForm] = useState({ name: "", email: "", password: "" })
  const [errors, setErrors] = useState({})

  const validate = () => {
    const e = {}
    if (!form.name.trim()) e.name = "Nama wajib diisi"
    else if (form.name.length < 3) e.name = "Minimal 3 karakter"

    if (!form.email.includes("@")) e.email = "Email tidak valid"

    if (form.password.length < 8) e.password = "Password minimal 8 karakter"

    return e
  }

  const handleSubmit = (e) => {
    e.preventDefault()
    const validationErrors = validate()
    if (Object.keys(validationErrors).length > 0) {
      setErrors(validationErrors)
      return
    }
    setErrors({})
    // Submit ke API...
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={form.name}
        onChange={e => setForm({...form, name: e.target.value})}
      />
      {errors.name && <span className="error">{errors.name}</span>}
      {/* repeat untuk email, password... */}
      <button type="submit">Daftar</button>
    </form>
  )
}

Cape ya? 10 field × ini = 200 baris boilerplate.

React Hook Form — Recommended

import { useForm } from "react-hook-form"
import { zodResolver } from "@hookform/resolvers/zod"
import { z } from "zod"

const schema = z.object({
  name: z.string().min(3, "Minimal 3 karakter"),
  email: z.string().email("Email tidak valid"),
  password: z.string().min(8, "Password minimal 8 karakter"),
})

function RegistrationForm() {
  const { register, handleSubmit, formState: { errors, isSubmitting } } =
    useForm({ resolver: zodResolver(schema) })

  const onSubmit = async (data) => {
    await fetch("/api/register", { method: "POST", body: JSON.stringify(data) })
  }

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("name")} />
      {errors.name && <span>{errors.name.message}</span>}

      <input {...register("email")} type="email" />
      {errors.email && <span>{errors.email.message}</span>}

      <input {...register("password")} type="password" />
      {errors.password && <span>{errors.password.message}</span>}

      <button disabled={isSubmitting}>{isSubmitting ? "Loading..." : "Daftar"}</button>
    </form>
  )
}

Lebih pendek, validasi declarative, error handling otomatis, performa bagus (cuma re-render field yang berubah).

Tips Validasi UX

Conditional Fields

const watchedJob = watch("job")

return (
  <>
    <select {...register("job")}>
      <option value="employed">Karyawan</option>
      <option value="freelance">Freelancer</option>
    </select>
    {watchedJob === "employed" && (
      <input {...register("companyName", { required: true })} placeholder="Nama Perusahaan" />
    )}
  </>
)

🎭 Analogi sehari-hari

Form manual itu kayak gerai bakso pinggir jalan — segala-galanya pemilik pakai sendiri: kasir, koki, pelayan, validator pesanan ("eh kembaliannya kurang"). Buat 5 customer per hari OK. Buat 50 customer/hari? Stress, kepala panas, salah hitung. Form library = POS system Tokopedia/GoFood: ada role spesialis (validator schema = Zod, state manager = react-hook-form, submit handler = onSubmit). Tiap komponen kerjaan tertentu, terkoordinasi otomatis. Lebih cepat, lebih jarang typo, scalable. Trade-off: belajar tools dulu, tapi sekali paham — produktivitas naik drastis.

⚠️ Jebakan yang sering ditemui

🎯 Pilih form approach — pakai mana?

  • 1-2 field, validasi simpel → useState manual ✓
  • 3-5 field, validasi medium → useState + helper validate() function
  • 6+ field, validasi kompleks, multi-step → React Hook Form + Zod (modern default)
  • Form dengan banyak conditional fields → React Hook Form + watch
  • Form dengan dynamic fields (array) → useFieldArray dari RHF
  • Form yang submit langsung tanpa validation → controlled biasa OK

Aturan: kalau form mulai > 100 baris kode, waktunya pindah ke library. Hemat waktu jangka panjang.

TL;DR: Form sederhana? useState + validate() function manual cukup. Form kompleks (6+ field, validasi rumit, conditional, multi-step)? React Hook Form + Zod — schema-based validation, error handling otomatis, performance bagus. Validasi saat submit dulu, switch ke onBlur/real-time setelah submit pertama. Disable submit button saat isSubmitting (cegah double submit). Frontend validation = UX, server validation TETAP WAJIB (security).

Yang akan kamu pelajari