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.