Form Handling

onSubmit ile formu göndermek, birden fazla alanı tek bir state nesnesiyle yönetmek, validation ve hata mesajları göstermek -- basit örneklerle.

Orta 13 dk
EN

Form Handling

Controlled Components dersinde tek bir input'u state'e bağlamayı gördük. Bu ders, gerçek bir formu -- gönderimi, birden fazla alanı, doğrulamayı ve hata mesajlarını -- baştan sona ele alıyor.

Formu Göndermek: onSubmit ile Değerleri Toplamak

Events dersinde gördüğümüz onSubmit, formlarda genellikle state'teki değeri kullanmak için kullanılır:

import { useState } from "react";

function FormSubmitExample() {
  const [name, setName] = useState("");
  const [submittedName, setSubmittedName] = useState(null);

  function handleSubmit(event) {
    event.preventDefault();
    // Form gönderildiğinde, o an state'te olan değeri kullanıyoruz --
    // controlled input olduğu için değerin ne olduğunu her zaman biliyoruz.
    setSubmittedName(name);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(event) => setName(event.target.value)}
        placeholder="İsmini yaz"
      />
      <button type="submit">Gönder</button>

      {submittedName && <p>Gönderilen: {submittedName}</p>}
    </form>
  );
}

Input controlled olduğu için, form gönderildiğinde name state'i zaten güncel -- ayrıca DOM'dan değeri okumaya gerek yok, doğrudan state'i kullanıyoruz.

Birden Fazla Alanı Yönetmek

Bir formda birden fazla input olduğunda, her biri için ayrı bir useState açmak yerine, hepsini TEK bir state nesnesinde toplamak daha yönetilebilir:

import { useState } from "react";

function MultiFieldFormExample() {
  // Her alan için ayrı bir useState açmak yerine, TÜM alanları tek bir
  // state nesnesinde tutuyoruz -- form büyüdükçe daha yönetilebilir.
  const [formData, setFormData] = useState({ name: "", email: "" });

  function handleChange(event) {
    const { name, value } = event.target;

    // Hangi input değiştiyse (input'un `name` attribute'una bakarak),
    // yalnızca o alanı güncelliyoruz, diğerlerini olduğu gibi kopyalıyoruz.
    setFormData({ ...formData, [name]: value });
  }

  return (
    <form>
      <input
        type="text"
        name="name"
        value={formData.name}
        onChange={handleChange}
        placeholder="İsim"
      />
      <input
        type="email"
        name="email"
        value={formData.email}
        onChange={handleChange}
        placeholder="E-posta"
      />
      <p>
        {formData.name} / {formData.email}
      </p>
    </form>
  );
}

event.target.name, değişen input'un name attribute'unu verir -- [name]: value (computed property name) ile, TEK bir handleChange fonksiyonuyla hangi alanın değiştiğini anlayıp yalnızca o alanı güncelleyebiliyoruz. Bu, State dersinde gördüğümüz immutability kuralına uygun: { ...formData, [name]: value }, eski nesneyi kopyalayıp yalnızca bir alanı değiştiren YENİ bir nesne oluşturuyor.

Basit Validation (Doğrulama)

Formu göndermeden önce, girilen değerlerin geçerli olup olmadığını kontrol etmek isteriz -- buna validation denir:

import { useState } from "react";

function RequiredFieldValidationExample() {
  const [name, setName] = useState("");
  const [error, setError] = useState("");

  function handleSubmit(event) {
    event.preventDefault();

    if (name.trim() === "") {
      setError("İsim boş bırakılamaz.");
      return;
    }

    setError("");
    console.log("Form gönderildi:", name);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(event) => setName(event.target.value)}
        placeholder="İsim"
      />
      <button type="submit">Gönder</button>

      {error && <p className="error">{error}</p>}
    </form>
  );
}

Gönderim anında (handleSubmit içinde) değeri kontrol ediyoruz; boşsa error state'ine bir mesaj yazıp fonksiyondan erken çıkıyoruz (return), formu asıl "göndermiyoruz".

Hata Mesajları Göstermek

Bir hata bulunduğunda, kullanıcıya bunu göstermek gerekir -- Conditional Rendering dersinde gördüğümüz && deseni burada tam yerine oturuyor:

import { useState } from "react";

function EmailFormatValidationExample() {
  const [email, setEmail] = useState("");
  const [error, setError] = useState("");

  // Basit bir e-posta biçimi kontrolü -- gerçek projelerde daha kapsamlı
  // kurallar da olabilir, burada yalnızca "@" ve bir nokta var mı bakıyoruz.
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

  function handleSubmit(event) {
    event.preventDefault();

    if (!emailPattern.test(email)) {
      setError("Geçerli bir e-posta adresi gir.");
      return;
    }

    setError("");
    console.log("Geçerli e-posta:", email);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(event) => setEmail(event.target.value)}
        placeholder="E-posta"
      />
      <button type="submit">Gönder</button>

      {error && <p className="error">{error}</p>}
    </form>
  );
}

error state'i doluysa (boş string değilse), {error && <p>...} ifadesi hata mesajını render eder; error boşsa hiçbir şey render edilmez. Bu örnekte ayrıca basit bir e-posta biçimi kontrolü de var -- bir regex (emailPattern.test(email)) ile.

Gönderim Öncesi Tüm Formu Doğrulamak

Birden fazla alanlı bir formda, her alanın kendi hatasını göstermesi gerekebilir:

import { useState } from "react";

function FullFormValidationExample() {
  const [formData, setFormData] = useState({ name: "", email: "" });
  const [errors, setErrors] = useState({});

  function handleChange(event) {
    const { name, value } = event.target;
    setFormData({ ...formData, [name]: value });
  }

  function validate() {
    // Her alan için ayrı bir hata mesajı üretip, hataların HEPSİNİ tek bir
    // nesnede topluyoruz -- her alanın kendi hatasını göstermesini sağlar.
    const newErrors = {};

    if (formData.name.trim() === "") {
      newErrors.name = "İsim boş bırakılamaz.";
    }

    if (formData.email.trim() === "") {
      newErrors.email = "E-posta boş bırakılamaz.";
    }

    return newErrors;
  }

  function handleSubmit(event) {
    event.preventDefault();

    const newErrors = validate();
    setErrors(newErrors);

    // Nesnenin hiç anahtarı yoksa, hiç hata yok demektir.
    if (Object.keys(newErrors).length === 0) {
      console.log("Form geçerli, gönderiliyor:", formData);
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <input
          type="text"
          name="name"
          value={formData.name}
          onChange={handleChange}
          placeholder="İsim"
        />
        {errors.name && <p className="error">{errors.name}</p>}
      </div>

      <div>
        <input
          type="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
          placeholder="E-posta"
        />
        {errors.email && <p className="error">{errors.email}</p>}
      </div>

      <button type="submit">Gönder</button>
    </form>
  );
}

validate() fonksiyonu, her alan için ayrı bir hata mesajı üretip hepsini TEK bir nesnede (newErrors) topluyor. Bu nesnenin hiç anahtarı yoksa (Object.keys(newErrors).length === 0), form geçerli demektir. Her input'un altında yalnızca KENDİ hatası (errors.name, errors.email) gösteriliyor -- bu da yine && ile conditional rendering.

Özet ve Terimler Sözlüğü

Bir form, onSubmit ile gönderilir; controlled input'lar sayesinde gönderim anında değerler zaten state'te hazır bulunur. Birden fazla alan tek bir state nesnesinde tutulup event.target.name ile hangi alanın değiştiği anlaşılır. Validation, genellikle gönderim anında yapılır; hatalar bir state'te (tek bir mesaj ya da alan başına bir nesne olarak) tutulup && ile conditional rendering'le gösterilir.

Terimler Sözlüğü

Validation (Doğrulama) — Girilen değerlerin beklenen kurallara uyup uymadığını kontrol etme süreci.

Computed Property Name{ [name]: value } gibi, bir nesnenin anahtarını bir değişkenden dinamik olarak belirleme söz dizimi.

Pratik Proje

Bu kategoride (Controlled Components, Form Handling) öğrendiğimiz kavramları bir arada kullanan, gerçek ve çalıştırılabilir bir örnek proje var: Forms Demo -- basit bir kayıt (sign up) formu.

Proje; value+onChange ile controlled input'ları, birden fazla alanı tek bir state nesnesinde yönetmeyi, onSubmit+preventDefault'u, ve gönderim öncesi tüm formu doğrulayıp her alanın kendi hatasını göstermeyi bir arada gösteriyor. Bilgisayarına indirip çalıştırabilir, kodunu satır satır inceleyebilirsin:

git clone https://github.com/cdurgun/react-course-projects.git
cd react-course-projects
npm install
cd projects/forms
npm run dev

react-course-projects deposu npm workspaces kullanır -- npm install yalnızca bir kez, depo kökünde çalıştırılır ve tüm proje klasörleri ortak bağımlılıkları paylaşır (her klasörde ayrı bir node_modules oluşmaz). Kök dizinde npm install'ı daha önce çalıştırdıysan, doğrudan cd react-course-projects/projects/forms yapıp npm run dev demen yeterli.