Hook Kuralları
Hook’lar React 16.8’deki yeni bir eklentidir. Bir sınıf yazmadan state ve diğer React özelliklerini kullanmanıza olanak sağlarlar.
Hook’lar JavaScript fonksiyonlarıdır, ancak bunları kullanırken iki kurala uymanız gerekir. Bu kuralları otomatik olarak uygulamak için bir linter eklentisi sunuyoruz:
Hook’ları Sadece En Üst Seviyede Çağırın
Döngülerde, koşullarda veya iç içe geçmiş fonksiyonlarda Hook çağrısı yapmayın. Bunun yerine, Hook’ları her zaman React fonksiyonunuzun en üst seviyesinde, herhangi bir return yapmadan önce kullanın. Bu kuralı uygulayarak, bir bileşenin her render edildiğinde Hook‘ların aynı sırada çağrıldığından emin olursunuz. React’in çoklu useState
ve useEffect
çağrıları arasındaki Hook’ların durumunu doğru şekilde korumasını sağlayan şey budur. (Merak ediyorsanız, bunu aşağıda detaylıca açıklayacağız.)
Hook’ları Sadece React Fonksiyonlarından Çağırın
Sıradan JavaScript fonksiyonlarında Hook’ları çağırmayın. Bunun yerine:
- ✅ React fonksiyon bileşenlerinden Hook’ları çağırabilirsiniz.
- ✅ Özel Hook’lardan Hook’ları çağırabilirsiniz. (bir sonraki sayfada bunları öğreneceğiz.)
Bu kuralı uygulayarak, bir bileşendeki tüm durum bilgisi mantığının kaynak kodundan açıkça görülebildiğinden emin olursunuz.
ESLint Eklentisi
Bu iki kuralı uygulayan eslint-plugin-react-hooks
adında bir ESLint eklentisi yayınladık. Denemek isterseniz, bu eklentiyi projenize ekleyebilirsiniz:
npm install eslint-plugin-react-hooks --save-dev
// Sizin ESLint yapılandırmanız
{
"plugins": [
// ...
"react-hooks"
],
"rules": {
// ...
"react-hooks/rules-of-hooks": "error", // Hook kurallarını kontrol eder
"react-hooks/exhaustive-deps": "warn" // Efekt bağımlılıklarını kontrol eder
}
}
Bu eklenti varsayılan olarak Create React App aracına dahil edilmiştir.
Kendi Hook’larınızı nasıl yazacağınızı açıklayan bir sonraki sayfaya şimdi atlayabilirsiniz. Bu sayfaya, bu kuralların ardındaki mantığı açıklayarak devam edeceğiz.
Açıklama
Daha önce öğrendiğimiz gibi, tek bir bileşende birden fazla State veya Efekt Hook’larını kullanabiliriz:
function Form() {
// 1. name state değişkenini kullan
const [name, setName] = useState('Onur');
// 2. Formun devamlılığını sağlamak için bir efekt kullan
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
// 3. surname state değişkenini kullan
const [surname, setSurname] = useState('Şuyalçınkaya');
// 4. Başlığı güncellemek için bir efekt kullan
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
// ...
}
Peki React, hangi state’in hangi useState
çağrısına karşılık geldiğini nasıl biliyor? Cevap, React’in Hook’ların çağrılma sırasına dayalı olmasıdır. Örneğimiz çalışıyor çünkü Hook çağrılarının sırası her render etmede aynı:
// ------------
// İlk render etme
// ------------
useState('Onur') // 1. name state değişkenini 'Onur' ile başlat
useEffect(persistForm) // 2. Formun devamlılığını sağlamak için bir efekt ekle
useState('Şuyalçınkaya') // 3. surname state değişkenini 'Şuyalçınkaya' ile başlat
useEffect(updateTitle) // 4. Başlığı güncellemek için bir efekt ekle
// -------------
// İkinci render etme
// -------------
useState('Onur') // 1. name state değişkenini oku (argüman yoksayılmıştır)
useEffect(persistForm) // 2. Formun devamlılığını sağlamak efekti değiştir
useState('Şuyalçınkaya') // 3. surname state değişkenini oku (argüman yoksayılmıştır)
useEffect(updateTitle) // 4. Başlığı güncellemek için efekti değiştir
// ...
Hook çağrılarının sırası render etmeler arasında aynı olduğu sürece, React bazı yerel state’leri bu çağrıların her biriyle ilişkilendirebilir. Ancak bir koşulun içine bir Hook çağrısı (örneğin, persistForm
efekti) koyarsak ne olur?
// 🔴 Bir koşul içerisinde Hook kullanarak ilk kuralı çiğniyoruz
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
İlk render etmede name !== ''
koşulu true
, bu yüzden bu Hook’u çalıştırıyoruz. Bununla birlikte, bir sonraki render etmede kullanıcı formu temizleyerek koşulu false
hale getirebilir. Artık render etme sırasında bu Hook’u atladığımız için, Hook çağrılarının sırası değişiyor:
useState('Onur') // 1. name state değişkenini oku (argüman yoksayılmıştır)
// useEffect(persistForm) // 🔴 Bu Hook atlandı!
useState('Şuyalçınkaya') // 🔴 2 (ama 3'tü). surname state değişkeni okunamadı
useEffect(updateTitle) // 🔴 3 (ama 4'tü). Efekt değiştirilemedi
React, ikinci useState
Hook çağrısı için ne döneceğini bilemezdi. React, bu bileşendeki ikinci Hook çağrısının, bir önceki render etme sırasında olduğu gibi, persistForm
efektine karşılık gelmesini bekliyordu, ancak artık gelmiyor. Bu noktadan itibaren, atladığımız çağrıdan sonraki her bir Hook çağrısı da birer birer kayıp, hatalara yol açacaktır.
Bu yüzden Hook’lar bileşenlerimizin en üst seviyesinde çağrılmalıdır. Eğer bir efekti koşullu olarak çalıştırmak istiyorsak, bu koşulu Hook’umuzun içerisine koyabiliriz:
useEffect(function persistForm() {
// 👍 Artık ilk kuralı çiğnemiyoruz
if (name !== '') {
localStorage.setItem('formData', name);
}
});
Eğer sunulan lint kuralını kullanırsanız, bu sorun için endişelenmenize gerek kalmadığını unutmayın. Ama artık Hook’ların neden bu şekilde çalıştığını ve kuralın hangi sorunları önlediğini de biliyorsunuz.
Sonraki Adımlar
Sonunda, kendi Hook’larınızı yazmayı öğrenmeye hazırız. Özel Hook’lar, React tarafından sağlanan Hook’ları kendi soyutlamalarınızla birleştirmenize ve farklı bileşenler arasındaki ortak durum mantığını yeniden kullanmanıza olanak sağlar.