Merhaba arkadaşlar, bugün React'ın o meşhur "Rules of Hooks" kurallarından birinin derinlerine ineceğiz. Özellikle karmaşık bir custom hook yazarken, içinde başka bir custom hook çağırmak istediğimde aklıma hep şu soru takılıyordu: "Acaba bu çağrı, hook kurallarını (top-level, conditional vs.) ihlal ediyor mu?" Bu sorunu çözmek için bulduğum yöntemi sizinle paylaşacağım.
Hook İçinde Hook: Kafa Karıştıran Senaryo
Diyelim ki bir `useUserData` hook'u yazdınız ve bu hook'un içinde, başka bir projeden aldığınız veya takım arkadaşınızın yazdığı `useApi` gibi bir custom hook'u kullanmak istiyorsunuz. `useApi` hook'unun da kendi içinde `useState`, `useEffect` gibi hook'ları kullandığını varsayalım. İşte tam bu noktada, `useUserData` hook'unuzu yazarken, `useApi`'yi doğru yerde (yani koşullu olmayan, top-level bir konumda) çağırdığınızdan nasıl emin olursunuz? Çünkü eğer yanlış yaparsanız, o meşhur "Invalid hook call" hatasıyla burun buruna gelirsiniz.
Çözüm: ESLint Plugin'i ve Manuel Kontrol
React, bu kuralları kontrol etmek için harika bir araç sunuyor: eslint-plugin-react-hooks. Bu plugin, hook kurallarını otomatik olarak kontrol eder ve ihlal durumunda sizi uyarır. Projenizde mutlaka yüklü ve aktif olmalı!
Ancak, bazen plugin'in gözünden kaçan veya geliştirme anında hızlıca kontrol etmek istediğiniz durumlar olabilir. İşte benim kullandığım pratik, manuel kontrol yöntemi:
Peki, yazdığım hook'un kurallara uygun olduğundan nasıl emin olurum? İşte benim adım adım kontrol listem:
1. Hook'unuzun adı `use` ile başlıyor mu? (Kural bu değil ama iyi bir başlangıç noktası. ESLint buna göre kuralları uygular.)
2. Tüm hook çağrılarınız (`useState`, `useEffect`, `useApi` gibi) hook fonksiyonunuzun en üst seviyesinde mi? Yani `if`, `for`, `while`, `switch` bloklarının veya `try-catch` yapılarının, callback fonksiyonlarının (`.then`, `onClick`) İÇİNDE DEĞİL mi?
3. Hook'unuzu kendiniz bir React fonksiyon bileşeni içinde veya başka bir custom hook içinde mi çağırıyorsunuz? Normal JavaScript fonksiyonlarında çağırmamalısınız.
Pro Tip: "Hook Flow" Zihinsel Modeli
Karmaşık hook'lar yazarken, her bir hook çağrısını bir "düğüm" gibi düşünün. Bu düğümlerin sırası, her render'da aynı kalmalı. React, bu düğümleri sırayla "hatırlar". Eğer bir render'da `useApi` düğümü 2. sırada, diğer render'da 3. sırada olursa, React kafası karışır ve hata verir. İç içe hook yazarken, bu "düğüm sırasının" asla değişmeyeceğinden emin olun.
Peki Ya Dinamik Hook Kullanmak İstiyorsam?
Bazen "şu koşula göre farklı bir hook kullanayım" diye düşünebilirsiniz. Bu doğrudan mümkün değil. Ancak alternatif bir pattern var:
Kısacası, hook'ları koşullu olarak çağıramazsınız, ama bir hook'tan dönen değeri veya fonksiyonu, başka bir hook'un içindeki `useEffect` gibi bir yerde koşullu olarak kullanabilirsiniz. Aradaki fark bu.
Sonuç olarak, custom hook içinde başka bir hook çağırmanın en güvenli yolu, ESLint kurallarını aktif tutmak ve hook'larınızı her zaman fonksiyonun en üst seviyesinde, sabit bir sırayla çağırmaktır.
Siz bu konuda ne düşünüyorsunuz? Karmaşık custom hook'lar yazarken başka hangi stratejileri izliyorsunuz? Ya da hiç "Invalid hook call" hatasını, iç içe hook kullanımından dolayı aldınız mı? Yorumlarda deneyimlerinizi paylaşın!
Diyelim ki bir `useUserData` hook'u yazdınız ve bu hook'un içinde, başka bir projeden aldığınız veya takım arkadaşınızın yazdığı `useApi` gibi bir custom hook'u kullanmak istiyorsunuz. `useApi` hook'unun da kendi içinde `useState`, `useEffect` gibi hook'ları kullandığını varsayalım. İşte tam bu noktada, `useUserData` hook'unuzu yazarken, `useApi`'yi doğru yerde (yani koşullu olmayan, top-level bir konumda) çağırdığınızdan nasıl emin olursunuz? Çünkü eğer yanlış yaparsanız, o meşhur "Invalid hook call" hatasıyla burun buruna gelirsiniz.
React, bu kuralları kontrol etmek için harika bir araç sunuyor: eslint-plugin-react-hooks. Bu plugin, hook kurallarını otomatik olarak kontrol eder ve ihlal durumunda sizi uyarır. Projenizde mutlaka yüklü ve aktif olmalı!
Ancak, bazen plugin'in gözünden kaçan veya geliştirme anında hızlıca kontrol etmek istediğiniz durumlar olabilir. İşte benim kullandığım pratik, manuel kontrol yöntemi:
JavaScript:
// Örnek: İç içe custom hook kullanımı
import { useState, useEffect } from 'react';
import useApi from './useApi'; // Diğer custom hook'umuz
function useUserData(userId) {
// ✅ DOĞRU: Hook'lar top-level'da çağrılıyor.
const [finalData, setFinalData] = useState(null);
const { data, loading, error } = useApi(`/users/${userId}`); // Başka bir custom hook
// 🚨 YANLIŞ ÖRNEK (Yapmayın!):
// if (userId) {
// const { data, loading, error } = useApi(`/users/${userId}`); // ❌ Koşul bloğu içinde!
// }
useEffect(() => {
if (data) {
// Gelen veriyi işle...
setFinalData(processData(data));
}
}, [data]);
return { finalData, loading, error };
}
Peki, yazdığım hook'un kurallara uygun olduğundan nasıl emin olurum? İşte benim adım adım kontrol listem:
1. Hook'unuzun adı `use` ile başlıyor mu? (Kural bu değil ama iyi bir başlangıç noktası. ESLint buna göre kuralları uygular.)
2. Tüm hook çağrılarınız (`useState`, `useEffect`, `useApi` gibi) hook fonksiyonunuzun en üst seviyesinde mi? Yani `if`, `for`, `while`, `switch` bloklarının veya `try-catch` yapılarının, callback fonksiyonlarının (`.then`, `onClick`) İÇİNDE DEĞİL mi?
3. Hook'unuzu kendiniz bir React fonksiyon bileşeni içinde veya başka bir custom hook içinde mi çağırıyorsunuz? Normal JavaScript fonksiyonlarında çağırmamalısınız.
Karmaşık hook'lar yazarken, her bir hook çağrısını bir "düğüm" gibi düşünün. Bu düğümlerin sırası, her render'da aynı kalmalı. React, bu düğümleri sırayla "hatırlar". Eğer bir render'da `useApi` düğümü 2. sırada, diğer render'da 3. sırada olursa, React kafası karışır ve hata verir. İç içe hook yazarken, bu "düğüm sırasının" asla değişmeyeceğinden emin olun.
Bazen "şu koşula göre farklı bir hook kullanayım" diye düşünebilirsiniz. Bu doğrudan mümkün değil. Ancak alternatif bir pattern var:
JavaScript:
function useDynamicFeature(isEnabled) {
const [state, setState] = useState(null);
// Koşula bağlı olarak EFFECT içinde işlem yapabilirsiniz.
useEffect(() => {
if (isEnabled) {
// `isEnabled` true olduğunda yapılacak işlemler...
// Buraya başka bir hook ÇAĞIRAMAZSINIZ, ama onun döndürdüğü değeri kullanabilirsiniz.
const externalData = useSomeExternalHook(); // ❌ YAPMAYIN! Burada hook çağrılmaz.
// Bunun yerine, hook'u bileşen seviyesinde çağırıp değerini buraya prop olarak geçin.
}
}, [isEnabled]);
return state;
}
Kısacası, hook'ları koşullu olarak çağıramazsınız, ama bir hook'tan dönen değeri veya fonksiyonu, başka bir hook'un içindeki `useEffect` gibi bir yerde koşullu olarak kullanabilirsiniz. Aradaki fark bu.
Sonuç olarak, custom hook içinde başka bir hook çağırmanın en güvenli yolu, ESLint kurallarını aktif tutmak ve hook'larınızı her zaman fonksiyonun en üst seviyesinde, sabit bir sırayla çağırmaktır.
Siz bu konuda ne düşünüyorsunuz? Karmaşık custom hook'lar yazarken başka hangi stratejileri izliyorsunuz? Ya da hiç "Invalid hook call" hatasını, iç içe hook kullanımından dolayı aldınız mı? Yorumlarda deneyimlerinizi paylaşın!