Hook kuralları iki tanedir: hook'ları yalnızca bileşeninin en üst düzeyinde çağır, asla bir koşulun, bir döngünün, iç içe bir fonksiyonun içinde ya da erken bir return'den sonra değil; ve onları yalnızca fonksiyon bileşenlerinden ya da özel hook'lardan çağır. Bu kurallar, React hook'ları çağırdığın sıraya göre birbirinden ayırdığı için vardır; bu yüzden bu sıra her render'da aynı olmalıdır.
React bir bileşenin hook'larının değerlerini o bileşene bağlı bir listede saklar. Her render'da listede dolaşır: ilk hook çağrısı ilk yuvayı, ikinci çağrı ikinci yuvayı alır ve böyle devam eder. Adlar yoktur, yalnızca konumlar vardır. Aşağıdaki örnek, bu fikrin birkaç satır düz JavaScript ile yazılmış oyuncak bir sürümüdür, React'in kendi kodu değil; böylece onu güvenle bozabilirsin.
İlk render üç yuvayı doldurur: Ada, Countess, 36. "Render without nickname"e tıkla: koşullu çağrı atlanır, bu yüzden age çağrısı 1. yuvayı alır ve Countess'i okur. Gerçek React bazı durumlarda uyuşmazlığı fark eder ve hata fırlatır, bazılarında ise yanlış değeri yanlış hook'a verir; bu daha kötüdür. toyUseState('Countess') çağrısını if'in üstüne taşı; iki düğme de doğru yaşı verir.
Kodda iki kural
function Profile({ user }) {
// Good: always called, always in this order
const [tab, setTab] = useState('posts');
const theme = useContext(ThemeContext);
useEffect(() => {
document.title = user.name;
}, [user.name]);
// Bad: inside a condition, a loop or a nested function
if (user.isAdmin) {
const [log, setLog] = useState([]); // breaks the order
}
}
Her zaman aynı sırayla ulaşılan bir hook çağrısı, herhangi bir erken return'den önce geldiği sürece gövdede nerede durursa dursun sorun değildir.
Koşullu çağrılan bir hook'u düzeltmek
Linter bunu React Hook "useEffect" is called conditionally. React Hooks must be called in the exact same order in every component render. olarak bildirir. Çözüm her zaman aynıdır: hook'u her seferinde çağır ve koşulu onun içine koy.
// Broken: the effect exists on some renders and not others
function Notifications({ user }) {
if (user) {
useEffect(() => {
console.log('subscribe for', user.name);
}, [user]);
}
return null;
}
Efekt her render'da tanımlanır ve içindeki if bir şey yapılıp yapılmayacağına karar verir. Birkaç kez oturum açıp kapat; Console her oturum açma için bir subscribe, her oturum kapatma için bir unsubscribe gösterir.
Erken bir return'den sonra hook'lar
Erken bir return de bir koşuldur. Bu, loading ilk kez false olduğunda "Rendered more hooks than during the previous render" hatasını üretir, çünkü o render bir öncekinin hiç çağırmadığı bir hook'a ulaşır. Bir render bir öncekinden daha az hook çağırdığında mesaj "Rendered fewer hooks than expected" olur.
// Broken: useState is skipped while loading
function Comments({ loading, comments }) {
if (loading) return <p>Loading...</p>;
const [expanded, setExpanded] = useState(false);
return <List items={comments} expanded={expanded} />;
}
Her hook return'ün üstünde durur, bu yüzden iki dal da aynı hook'ları çağırır. "Toggle loading"e tıkla, listeyi genişlet, sonra loading'i iki kez değiştir: liste genişletilmiş olduğunu hatırlar, çünkü state hiç atlanmadı.
Bir döngüde hook'lar
Bir döngüde her öğe için bir kez useState çağıramazsın, çünkü öğe sayısı değişir. Her öğeye kendi bileşenini ver ve hook'u orada çağır; her örneğin kendi yuvaları vardır.
"Invalid hook call"
Mesaj "Invalid hook call. Hooks can only be called inside of the body of a function component." ile başlar. Üç olağan nedeni vardır:
- Hook, React bir fonksiyon bileşenini render etmiyorken çalışıyor. Bir olay işleyicisinde, bir efekt callback'inin içinde, bir class bileşeninde ya da modül düzeyinde. Onu bileşen gövdesine ya da bir özel hook'a taşı ve özel hook'u bileşenden çağır.
reactvereact-domsürümleri eşleşmiyor. İkisinin de aynı sürümünü kur.- İki React kopyası var. Bağlanmış yerel bir paket ya da kendi React'ini paketleyen bir kütüphane ikinci bir kopya getirir ve bir kopyadaki hook'lar diğerinin renderer'ıyla çalışmaz.
npm ls reactçalıştır ve yalnızca bir sürümün göründüğünden emin ol.
npm ls react
Buradaki önizleme bu mesajları yazdırmayan bir production derlemesi gibi çalışır: bir olay işleyicisinden çağrılan bir hook "Cannot read properties of null" gibi düz bir TypeError ile başarısız olur ve React'in kendi hataları kısa "Minified React error" kodları olarak gelir. Tam metni okumak için uygulamanı geliştirme modunda çalıştır.
Bırak linter uygulasın
eslint-plugin-react-hooks sen yazarken iki kuralı da kontrol eder, ayrıca efekt bağımlılık dizilerini de kontrol eder. Vite'ın React şablonu ve Next.js'in ESLint yapılandırması onu içerir. Düz (flat) bir ESLint yapılandırmasında önerilen ön ayarını aç:
// eslint.config.js
import reactHooks from 'eslint-plugin-react-hooks';
import { defineConfig } from 'eslint/config';
export default defineConfig([reactHooks.configs.flat.recommended]);
İki temel kural react-hooks/rules-of-hooks (bir hata) ve react-hooks/exhaustive-deps'tir (bir uyarı). Sürüm 7'den beri ön ayar, React Compiler'dan gelen react-hooks/purity ve react-hooks/refs gibi, saf olmayan render'ları ve render sırasında ref okumayı işaretleyen kuralları da açar. Kuralları tek tek seçmek için eklentiyi plugins: { 'react-hooks': reactHooks } olarak kaydet ve istediğin kuralları listele.
Eklenti hook'ları adlarına göre bulur: adı use ve ardından bir büyük harfle başlayan her fonksiyon bir hook olarak ele alınır ve onu yalnızca bileşenler (büyük harfli adlar) ve diğer use fonksiyonları çağırabilir. Özel hook'ların öneki kullanmak zorunda olmasının nedeni budur.
İstisna: use
React 19'da eklenen use bir promise'i ya da bir context'i okur ve diğer tüm hook'ların aksine bir koşulun ya da bir döngünün içinde çağrılabilir. Yine de render sırasında, bir bileşenin ya da bir hook'un içinde çağrılmalıdır.
Temayı değiştirdiğinde yalnızca temalı başlığın rengi değişir. Aynısını if içinde useContext ile dene; linter onu işaretler. use hook'u sayfası onunla promise okumayı anlatıyor.
Sıkça Sorulan Sorular
Hook kuralları nelerdir?
İki kural. Hook'ları yalnızca bir fonksiyon bileşeninin ya da özel hook'un en üst düzeyinde çağır; asla koşulların, döngülerin, iç içe fonksiyonların içinde ya da erken bir return'den sonra değil. Ve hook'ları yalnızca fonksiyon bileşenlerinden ya da özel hook'lardan çağır, sıradan fonksiyonlardan ya da class bileşenlerinden değil.
Hook'lar neden koşullu olarak çağrılamaz?
React hook'larını adlarıyla tanımaz. Değerlerini bir listede saklar ve çağrıların gerçekleştiği sırayla geri verir. Bir koşul bir çağrıyı atlarsa sonraki her hook kendisinden önceki hook'a ait değeri okur.
"Rendered more hooks than during the previous render" ne demek?
Bir render kendisinden önceki render'dan daha fazla hook çağırdı; genellikle bir hook bu sefer farklı çalışan bir if'in içinde ya da erken bir return'den sonra durduğu için. Her hook'u koşulun üstüne taşı ve bunun yerine koşulu hook'un içine koy.
"Invalid hook call" nasıl düzeltilir?
Üç şeyi kontrol et: hook, React bir fonksiyon bileşenini render ederken çalışıyor mu (bir olay işleyicisinde, bir efekt callback'inde, bir class bileşeninde ya da modül düzeyinde değil), react ve react-dom sürümleri eşleşiyor mu ve uygulamada yalnızca bir React kopyası var mı (npm ls react onları listeler).
use hook'u koşullu olarak çağrılabilir mi?
Evet. use (React 19) tek istisnadır: onu if bloklarının ve döngülerin içinde çağırabilirsin. Yine de bir olay işleyicisinde değil, bir bileşenin ya da bir hook'un içinde çağrılmalıdır.