React’te useState Hook’u: Kuramsal Temeller, Uygulama Desenleri ve İleri Düzey İpuçları
- Tarık Tunç

- 22 Haz
- 8 dakikada okunur
Güncelleme tarihi: 9 Eyl
useState Hook’u, fonksiyonel bileşenlerde yerel durumun (state) bildirime dayalı ve deterministik biçimde yönetilmesini sağlar. Bu çalışma, useState’in kavramsal temellerini açıklamakta; ilk değer ataması, çoklu state deseni, önceki değere bağlı güncelleme, immutability ilkesi ve yan etkilerin (useEffect) state değişimleriyle koordinasyonu gibi konuları örneklerle ele almaktadır.
Ek olarak, yaygın hatalar, performans optimizasyonları, erişilebilirlik boyutu, TypeScript ile tip güvenliği ve batched (toplu) güncellemeler gibi ileri düzey başlıklar tartışılmaktadır.
Anahtar Kelimeler
React, Hooks, useState, durum yönetimi, fonksiyonel bileşen, immutability, batched updates, TypeScript, erişilebilirlik
1. Giriş
Modern arayüz geliştirmede durum yönetimi, kullanıcı etkileşimlerinin tutarlı ve öngörülebilir şekilde işlenmesi için zorunludur. React, sınıf bileşenlerindeki setState yaklaşımını fonksiyonel bileşenlere taşıyan Hooks mimarisiyle, daha yalın bir API ve daha güçlü soyutlamalar sunar. Bu mimarinin en temel öğesi useState Hook’udur.
2. Kuramsal Çerçeve: useState’in Anlambilimi
useState, çağrıldığı bileşen örneğine ait tekil bir durum hücresi oluşturur:
const [value, setValue] = useState(initialValue);
value: Anlık durum değeridir.
setValue(updater): Durum değişikliğini bildirir ve yeniden render sürecini tetikler.
İmmutability: Durum değerleri doğrudan mutasyona uğratılmaz; yeni bir değer üretilir.
Batched Updates: Aynı olay döngüsü içinde yapılan birden çok güncelleme React tarafından toplulaştırılabilir (batched), bu da gereksiz render maliyetini azaltır.
3. Yöntem ve Temel Desenler
3.1. Temel Kullanım
import { useState } from "react";
function Example() {
const [title, setTitle] = useState("Merhaba, React!");
return (
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Başlık"
/>
);
}
3.2. İlk Değer Atamaları ve Türler
Başlangıç değeri, durumun varsayılan semantiğini belirler.
const [text, setText] = useState("Merhaba, React!");
const [count, setCount] = useState(0);
const [ready, setReady] = useState(false);
const [items, setItems] = useState<string[]>([]); // TS ile örnek
const [meta, setMeta] = useState({ author: "", tags: [] });
3.3. Çoklu State Kullanımı ve Gruplama
Birden fazla bağımsız alan:
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
İlişkili alanları tek nesnede gruplama (kohezyon yüksek, güncellemede dikkat):
const [user, setUser] = useState({ username: "", password: "" });
// Kısmi güncelleme:
setUser((prev) => ({ ...prev, username: "alice" }));
Not: Nesne durumlarında spread ile kopyalama zorunludur; doğrudan user.username = ... mutasyondur ve kaçınılmalıdır.
3.4. Önceki Değere Bağlı (Fonksiyonel) Güncelleme
Asenkron ve toplu güncelleme koşullarında tutarlılık sağlar.
const [count, setCount] = useState(0);
const increment = () => {
setCount((prev) => prev + 1);
setCount((prev) => prev + 1); // 2 artar; prev güvenlidir
};
3.5. State Değişikliklerini İzlemek: useEffect ile
import { useEffect, useState } from "react";
const [count, setCount] = useState(0);
useEffect(() => {
console.log("count değişti:", count);
}, [count]); // yalnızca count değiştiğinde tetiklenir
4. Uygulama Örnekleri
4.1. Basit Sayaç
import { useState } from "react";
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount((p) => p + 1)}>Artır</button>
<button onClick={() => setCount((p) => p - 1)}>Azalt</button>
<button onClick={() => setCount(0)}>Sıfırla</button>
</div>
);
}
4.2. Form Girdileri (Kontrollü Bileşenler)
import { useState } from "react";
export default function LoginForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log({ username, password });
setUsername("");
setPassword("");
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
placeholder="Kullanıcı Adı"
value={username}
onChange={(e) => setUsername(e.target.value)}
aria-label="Kullanıcı adı"
/>
<input
type="password"
placeholder="Şifre"
value={password}
onChange={(e) => setPassword(e.target.value)}
aria-label="Şifre"
/>
<button type="submit">Giriş Yap</button>
</form>
);
}
4.3. Tema Değiştirici
import { useState } from "react";
export default function ThemeSwitcher() {
const [theme, setTheme] = useState("light");
const toggleTheme = () => setTheme((t) => (t === "light" ? "dark" : "light"));
return (
<div className={`App ${theme}`}>
<button onClick={toggleTheme}>Tema Değiştir</button>
<p>Aktif tema: {theme}</p>
</div>
);
}
4.4. Nesne Durumu ve Kısmi Güncelleme
import { useState } from "react";
export default function PersonCard() {
const [person, setPerson] = useState({ name: "", age: 0 });
const updateName = (newName) =>
setPerson((prev) => ({ ...prev, name: newName }));
return (
<div>
<input
value={person.name}
onChange={(e) => updateName(e.target.value)}
placeholder="Ad"
/>
<p>Yaş: {person.age}</p>
</div>
);
}
5. Yaygın Hatalar ve Çözüm Önerileri
Doğrudan Mutasyon
Yanlış: user.name = "Ali"; setUser(user);
Doğru: setUser((p) => ({ ...p, name: "Ali" }));
Sonsuz Döngüye Yol Açan Güncellemeler
useEffect(() => { setCount((p) => p + 1); // [count] bağımlılığıyla döngü oluşturur }, [count]);
Etkiyi yalnızca gerekliyken tetikleyin veya bağımlılıkları gözden geçirin.
Eski Kapanım (Stale Closure)
Olay işleyicilerinde eski state’in yakalanması sorun yaratabilir. Fonksiyonel güncelleme kullanın veya bağımlılıkları doğru belirtin.
Büyük Nesnelerde Kopya Maliyeti
Derin yapıların her güncellemede kopyalanması pahalı olabilir; durumu parçalara ayırmak (çoklu useState) çoğu kez daha etkilidir.
Kontrolsüz/Kontrollü Karışıklığı
Bir input’u hem defaultValue hem value ile yönetmeyin. Tamamen kontrollü veya tamamen kontrolsüz bir model seçin.
Render İçinde Ağır Hesaplama
useMemo/useCallback ile yeniden hesaplamaları sınırlayın:
const expensive = useMemo(() => compute(data), [data]);
6. Performans, Erişilebilirlik ve Kullanılabilirlik
Batched Updates: Aynı olay döngüsündeki setState çağrıları toplulaştırılır; prev formu hatasız artış/azalış sağlar.
Re-render Sınırlandırma: Durumu bileşen hiyerarşisinde doğru yere konumlandırın; gereksiz alt bileşen renderlarını memo ile azaltın.
Erişilebilirlik (a11y): Form öğelerinde aria-label, ilişkilendirilmiş <label> ve anlamlı buton metinleri kullanın.
Hata Durumları: Kullanıcı geri bildirimi için error ve loading state’lerini ayrı tutun; bu, kodun niyetini netleştirir.
7. İleri Konular
7.1. TypeScript ile Tip Güvenliği
const [items, setItems] = useState<Array<{ id: string; qty: number }>>([]);
setItems((prev) => prev.map((it) =>
it.id === target ? { ...it, qty: it.qty + 1 } : it
));
7.2. useState vs. useReducer
useState: Basit, yerel ve az kurallı durumlar.
useReducer: Birden fazla alan, karmaşık geçiş kuralları ve tek noktadan güncelleme mantığı gerektiğinde tercih edilir.
7.3. Transizyonlar ve Kullanıcı Deneyimi
Concurrent özelliklerle transizyonlar, acil ve acil olmayan güncellemeleri ayırmaya imkân tanır (örn. arama yazarken liste filtreleme).
8. Sonuç
useState, React’in fonksiyonel paradigması içinde yerel durum yönetiminin çekirdeğini oluşturur. Fonksiyonel güncelleme kalıbı, immutability ve batched güncellemelerle birlikte kullanıldığında, uygulamalar tutarlı, performanslı ve bakımı kolay bir yapıya kavuşur. Doğru modelleme (çoklu state vs. nesne), etkili yan etki koordinasyonu (useEffect) ve tip güvenliği (TypeScript) ile useState hem öğretici hem de üretim senaryolarında güvenle tercih edilir.
Ek A: Metinde Geçen Düzgünleştirilmiş Kodlar (Toplu)
Sayaç (düzeltilmiş import ve tırnaklar):
import React, { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount((p) => p + 1)}>Artır</button>
<button onClick={() => setCount((p) => p - 1)}>Azalt</button>
</div>
);
}
export default Counter;
LoginForm (erişilebilirlik ekleriyle):
import React, { useState } from "react";
function LoginForm() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const handleSubmit = (e) => {
e.preventDefault();
console.log("Kullanıcı adı:", username);
console.log("Şifre:", password);
setUsername("");
setPassword("");
};
return (
<form onSubmit={handleSubmit}>
<label>
Kullanıcı Adı
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
placeholder="Kullanıcı Adı"
/>
</label>
<label>
Şifre
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Şifre"
/>
</label>
<button type="submit">Giriş Yap</button>
</form>
);
}
export default LoginForm;
Tema Değiştirici:
import React, { useState } from "react";
function ThemeSwitcher() {
const [theme, setTheme] = useState("light");
const toggleTheme = () => setTheme((t) => (t === "light" ? "dark" : "light"));
return (
<div className={`App ${theme}`}>
<button onClick={toggleTheme}>Tema Değiştir</button>
<p>Aktif tema: {theme}</p>
</div>
);
}
export default ThemeSwitcher;
Önceki Değere Dayalı Artış:
const [count, setCount] = useState(0);
const increment = () => setCount((prev) => prev + 1);
Nesne Durumu – Kısmi Güncelleme:
const [person, setPerson] = useState({ name: "", age: 0 });
const updateName = (newName) =>
setPerson((prev) => ({ ...prev, name: newName }));9. useState ve Alternatifleri: Karar Matrisi
useState her problemi çözmek için değil, yerel ve basit durumlar için idealdir. Aşağıdaki karar matrisi, doğru aracı seçmenize yardımcı olur:
useState: Birkaç alan, düşük etkileşim karmaşıklığı, basit geçiş kuralları.
useReducer: Çok adımlı/koşullu geçiş mantığı, tek merkezli güncelleme (ör. form sihirbazı).
Context + useState: Birkaç bileşene yayılan ama basit kalan ortak durum.
State makinesi/kütüphaneler (XState vb.): Kesin durum diyagramı, karmaşık akış ve geçişler.
Sunucu odaklı durum (tanımlı cache katmanları, React Query, SWR): Uzak veri eşitlemesi ve önbellekleme semantiği.
Örnek – useReducer ile geçiş mantığı netliği:
function formReducer(state, action) {
switch (action.type) {
case "name":
return { ...state, name: action.value };
case "age":
return { ...state, age: Number(action.value) || 0 };
case "reset":
return { name: "", age: 0 };
default:
return state;
}
}
10. Durum Kaynağı ve Yukarı Taşıma (Lifting State Up)
Durumun tek doğruluk kaynağı (single source of truth) nerede olmalıdır?
Durum, en çok kullanan bileşenin üstüne taşınmalı ve aşağıya props olarak aktarılmalıdır.
Aynı bilgi iki yerde tutulmamalı; türetilmiş değerler hesaplanmalı, saklanmamalıdır.
Türetilmiş Durum Anti-Pattern (kaçının):
// Kaçın: hem list hem filteredList tutuluyor
const [list, setList] = useState(items);
const [filtered, setFiltered] = useState(items.filter(...)); // redundant
Doğru:
const filtered = useMemo(() => list.filter(...), [list, query]);
11. Form Yönetimi: Kontrollü, Yarı-Kontrollü ve Kontrolsüz
Kontrollü: value ve onChange ile UI ←→ state birebir eşleşir. Doğrulama ve maskeleme için idealdir.
Kontrolsüz: defaultValue + ref ile DOM kaynaklıdır; çok büyük formlarda performans için düşünülebilir.
Yarı-kontrollü: Stratejik alanlar kontrollü, diğerleri kontrolsüzdür; karmaşık formlarda denge sağlar.
Yarı-kontrollü örnek:
<input defaultValue="TR" ref={countryRef} />
<input value={email} onChange={(e) => setEmail(e.target.value)} />
12. Toplu Güncellemeler ve Zamanlama Semantiği
React, olay işleyicisi içinde yapılan birden fazla setState çağrısını toplulaştırır. prev formu, yarış koşullarını önler.
setCount((p) => p + 1);
setCount((p) => p + 1); // count +2, deterministik
Mikro görev kuyruğu–makro görev farkı:setTimeout veya await sonrasında güncellemeler yeni bir döngüde işlenir; yine de prev güvenli modeldir.
13. Performans Optimizasyonu: Pratik Denetim Listesi
Durumu parçala: Büyük nesne yerine birden çok küçük useState.
Türetilmiş veriyi hesapla: useMemo ile pahalı hesapları sınırla.
Fonksiyon referanslarını sabitle: Çocuklara prop olarak geçen işlevlerde useCallback.
Render sınırla: React.memo ile değişmeyen alt bileşenleri koru.
Sık güncellenen alanları yakın tut: State’i en yakın ortak ataya taşı.
Gözlemler: Profiler ile “commit” sürelerini ve yeniden render nedenlerini izle.
14. Erişilebilirlik (a11y) ve Uluslararasılaştırma (i18n)
Form kontrolleri için programatik etiket: <label htmlFor="id"> ve aria-*.
Durum değişimlerini duyur: Gerekirse aria-live="polite" bölgesi kullan.
useState ile dil/yerel ayar tutuyorsan, metinleri değil anahtarları sakla; çeviri katmanında çöz.
15. Test Edilebilirlik: Birim ve Bütünleşik Testler
Birim (Jest, RTL):
Başlangıç durumunu doğrula.
Kullanıcı etkileşimi simüle ederek state değişimini gözlemle.
Yantı (effects) ve zamanlayıcılar: jest.useFakeTimers() ile deterministik test yaz.
E2E (Playwright/Cypress): Durumun UI’ye yansımasını senaryo bazlı doğrula.
Örnek – RTL ile sayaç testi (özet):
render(<Counter />);
fireEvent.click(screen.getByText(/Artır/i));
expect(screen.getByText(/Count: 1/)).toBeInTheDocument();
16. Tip Güvenliği: TypeScript Tasarım Notları
Birlik türleri ile durum varyantlarını ifade edin:
type Status = "idle" | "loading" | "success" | "error";
const [status, setStatus] = useState<Status>("idle");
Kısmi güncellemeler için açık tipler:
type Person = { name: string; age: number };
const [person, setPerson] = useState<Person>({ name: "", age: 0 });
17. Kalıcılık (Persistence) ve Eşitleme
Yerel durumu kalıcı kılmak için depolarla entegre edilebilir:
function usePersistentState(key, initial) {
const [value, setValue] = useState(() => {
const raw = localStorage.getItem(key);
return raw ? JSON.parse(raw) : initial;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
Not: Gizlilik ve güvenlik gereksinimlerini göz ardı etmeyin; hassas veriler için localStorage uygun değildir.
18. Sık Sorulan Sorular (Seçki)
S: Aynı olayda arka arkaya setState neden tek artış gibi davranıyor?C: Olay döngüsü içinde güncellemeler toplulaştırılır. Fonksiyonel güncelleme (prev => prev + 1) bu nedenle önerilir.
S: Nesne durumlarında neden “kısmi patch” yok?C: React, durumun değiştirilemez olmasını teşvik eder; “kısmi patch” API’si mutasyon yanılgısı doğurabilir. Spread ile yeni nesne üretmek net ve izlenebilir bir modeldir.
S: Büyük formlarda performans düşerse ne yapmalı?C: Alanları kontrolsüz hale getirmek, onBlur-temelli senkronizasyon veya sanal listeleme tekniklerini değerlendirin.
19. Sonuç ve Öneriler
useState, fonksiyonel bileşen mimarisinin en küçük ama en kritik yapı taşıdır. Doğru modelleme (yerel–global ayrımı), fonksiyonel güncellemeler, immutability ve batched semantiğin kavranması; performans, doğruluk ve bakım kolaylığını doğrudan artırır. Üretim ortamlarında, useState çoğu kez yeterlidir; karmaşıklık eşiği aşıldığında useReducer, Context veya üst seviye durum çözümleriyle mimarinin evrilmesi önerilir.
20. Ek B: İleri Uygulama – Debounce ile Arama Alanı
import { useEffect, useMemo, useState } from "react";
function useDebounced(value, delay = 300) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const id = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(id);
}, [value, delay]);
return debounced;
}
export default function SearchBox() {
const [query, setQuery] = useState("");
const debouncedQuery = useDebounced(query, 400);
useEffect(() => {
if (!debouncedQuery) return;
// fetch(`/api?q=${debouncedQuery}`)
}, [debouncedQuery]);
const placeholder = useMemo(() => "Ürün, kategori, marka…", []);
return (
<input
value={query}
placeholder={placeholder}
onChange={(e) => setQuery(e.target.value)}
aria-label="Arama kutusu"
/>
);
}React dünyasında useState Hook’u, fonksiyonel bileşenlerde durum (state) yönetimini kolaylaştıran en temel yapı taşıdır. Bu yazı boyunca useState’in kuramsal temelleri, kullanım desenleri ve ileri düzey uygulamaları sistematik biçimde ele alındı.
Temel Kullanım: useState, bir bileşende durum değişkeni ve onu güncelleyen fonksiyon döndürür.
İlk Değer Ataması: Sayılar, dizeler, nesneler veya diziler gibi farklı veri türleri başlangıç değeri olarak atanabilir.
Çoklu State Yönetimi: Birden fazla state bağımsız olarak tanımlanabilir veya ilişkili alanlar nesne halinde gruplanabilir.
Fonksiyonel Güncelleme: Önceki state değerine dayalı güncellemeler, asenkron işlemler ve batched updates sırasında güvenilir sonuç sağlar.
useEffect ile İzleme: State değişiklikleri, useEffect aracılığıyla yan etkilerle (ör. API çağrısı, event listener) ilişkilendirilebilir.
Yaygın Hatalar: Doğrudan mutasyon, yanlış dependency array kullanımı veya kontrolsüz/kontrollü bileşen karışıklığı hatalara yol açar.
Performans ve İmmutability: Durum parçalama, memoization (useMemo, useCallback), batched updates ve immutability ilkesi performans optimizasyonu sağlar.
İleri Konular: useReducer ile karmaşık mantık, Context API ile ortak durum, TypeScript ile tip güvenliği, localStorage ile kalıcı state gibi genişletilmiş senaryolar mümkündür.
Sonuç olarak, useState yalnızca temel bir API değil, aynı zamanda React mimarisinde güvenilir, öngörülebilir ve sürdürülebilir durum yönetimi için kritik bir bileşendir.
Kapanış Mesajı
Sevgili okuyucular,
Bu yazıda React’in en önemli yapı taşlarından biri olan useState Hook’unu hem teorik hem de pratik açıdan kapsamlı şekilde inceledik. Doğru kullanıldığında useState, yalnızca küçük ölçekli etkileşimlerde değil, aynı zamanda daha büyük ve karmaşık uygulamalarda da verimli ve hatasız durum yönetiminin temelini oluşturur.
React ile yolculuğunuzda, öğrendiklerinizi pratik örneklerle pekiştirmenizi ve kendi projelerinizde farklı senaryoları deneyerek bu bilgileri uygulamanızı öneririm. Unutmayın: Sağlam bir state yönetimi, kullanıcı deneyiminin kalitesini doğrudan etkileyen unsurlardan biridir.
🚀 Bir sonraki adımda, useReducer ve Context API gibi gelişmiş durum yönetimi yaklaşımlarını ele alacağız.
Sevgiler,Tarık Tunç



