React Forms

๐Ÿ“‹ Table of Contents โ–พ
  1. React Forms
  2. Controlled Inputs
  3. Form Validation

React Forms

HTML forms maintain their own state in the DOM. In React, we prefer controlled components โ€” the React state is the single source of truth for form values.

Controlled Inputs

function BasicForm() {
  const [form, setForm] = useState({
    username:  "",
    email:     "",
    password:  "",
    role:      "user",
    subscribe: false,
    bio:       "",
  });

  // Single handler for all field types
  const handleChange = (e) => {
    const { name, value, type, checked } = e.target;
    setForm(prev => ({
      ...prev,
      [name]: type === "checkbox" ? checked : value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log("Submitted:", form);
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* Text input */}
      <input name="username" value={form.username} onChange={handleChange} />

      {/* Email */}
      <input name="email" type="email" value={form.email} onChange={handleChange} />

      {/* Password */}
      <input name="password" type="password" value={form.password} onChange={handleChange} />

      {/* Select */}
      <select name="role" value={form.role} onChange={handleChange}>
        <option value="user">User</option>
        <option value="admin">Admin</option>
        <option value="moderator">Moderator</option>
      </select>

      {/* Checkbox */}
      <label>
        <input name="subscribe" type="checkbox" checked={form.subscribe} onChange={handleChange} />
        Subscribe to newsletter
      </label>

      {/* Textarea */}
      <textarea name="bio" value={form.bio} onChange={handleChange} />

      <button type="submit">Register</button>
    </form>
  );
}

Form Validation

function LoginForm() {
  const [values, setValues]   = useState({ email: "", password: "" });
  const [errors, setErrors]   = useState({});
  const [touched, setTouched] = useState({});

  const validate = (fields) => {
    const errs = {};
    if (!fields.email.includes("@"))  errs.email    = "Enter a valid email";
    if (fields.password.length < 8)   errs.password = "Min 8 characters";
    return errs;
  };

  const handleChange = (e) => {
    const updated = { ...values, [e.target.name]: e.target.value };
    setValues(updated);
    if (touched[e.target.name]) {
      setErrors(validate(updated)); // Live validate after first blur
    }
  };

  const handleBlur = (e) => {
    setTouched(prev => ({ ...prev, [e.target.name]: true }));
    setErrors(validate(values));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    setTouched({ email: true, password: true }); // Show all errors
    const errs = validate(values);
    setErrors(errs);
    if (Object.keys(errs).length === 0) {
      console.log("Login:", values);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        name="email" type="email" value={values.email}
        onChange={handleChange} onBlur={handleBlur}
      />
      {errors.email && <p className="error">{errors.email}</p>}

      <input
        name="password" type="password" value={values.password}
        onChange={handleChange} onBlur={handleBlur}
      />
      {errors.password && <p className="error">{errors.password}</p>}

      <button type="submit">Log in</button>
    </form>
  );
}
TipFor complex forms in real projects, consider React Hook Form (npm install react-hook-form). It reduces re-renders, simplifies validation, and integrates with Zod or Yup for schema validation.