React Hooks, sınıf yazmadan işlevsel bileşenlerde durum, efektler, bağlam ve diğer React özelliklerini kullanmanıza olanak tanıyan işlevlerdir. React 16.8’de tanıtılan hook’lar, HOC ve render props gibi karmaşık kalıpların yerini alarak geliştirme yaklaşımını kökten değiştirdi. React, 2024’e göre, tüm bileşen yönetimi senaryolarını kapsayan 15 yerleşik hook bulunmaktadır.
Önemli Noktalar
React Hooks, sınıf oluşturmadan çerçevenin iç mekanizmalarına “bağlanmanıza” olanak tanıyan, React’e yerleşik işlevler kümesidir. React 16.8’den önce, işlevsel bileşenler yalnızca props alabilir ve JSX döndürebilirdi — herhangi bir karmaşık mantık için bir sınıf bileşeni gerekiyordu.
Sınıf bileşenlerinin sorunu ilgili mantığın bölünmesidir. Aynı varlıkla ilgili kod (örneğin bir WebSocket aboneliği) zorla componentDidMount, componentDidUpdate ve componentWillUnmount arasında bölünüyordu. Hook’lar bunu çözer: tüm mantık tek bir useEffect’te gruplandırılır.
İkinci sorun mantığın yeniden kullanımıdır. Sınıf bileşenleri arasında durum mantığını yeniden kullanmak için HOC (Üst Düzey Bileşenler) veya render props uygulamak gerekiyordu — her iki kalıp da bileşen ağacında ekstra iç içelik yaratıyordu. Özel hook’lar bunu iç içelik olmadan çözer.
useState en temel hook’tur. Bir başlangıç değeri alır ve bir demet döndürür: geçerli değer ve onu güncellemek için bir işlev. Sınıflardaki this.setState’in aksine, useState nesneleri birleştirmez — yeni durum, belirli bir useState çağrısı için önceki durumu tamamen değiştirir.
import React, { useState } from 'react';
function Form() {
const [text, setText] = useState('');
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
setSubmitting(true);
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ text })
});
setSubmitting(false);
}
return (
<div>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={handleSubmit}
disabled={submitting}>
{submitting ? 'Saving...' : 'Submit'}
</button>
</div>
);
}
useState setter’ı, önceki değeri alan ve yeni bir değer döndüren bir işlevi kabul edebilir. İşlevsel biçim, yeni durum öncekinden hesaplandığında ve toplu setState çağrıları mümkün olduğunda zorunludur — her çağrı geçerli önceki değeri alacaktır.
useEffect, işlevsel bileşenlerde yan etkiler gerçekleştirmek için bir hook’tur. İki argüman alır: bir efekt işlevi ve bir bağımlılık dizisi. React, efekti her render’dan sonra çalıştırır, ancak bağımlılıklar belirtildiğinde — yalnızca bunlardan biri değiştiğinde.
Boş bir bağımlılık dizisi [], efektin ilk render’dan sonra bir kez çalıştığı anlamına gelir (componentDidMount’a benzer). Efektten bir işlev döndürmek, unmount sırasında (componentWillUnmount’a benzer) ve efekti yeniden çalıştırmadan önce çalışan bir temizleme işlevidir.
import React, { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`wss://chat/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)]);
};
// Temizlik - soketi kapat
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
useEffect bağımlılıkları, efekt ile dış dünya arasındaki tek bağlantıdır. Efekt, props veya durumdan bir değişken kullanıyorsa, bağımlılık dizisinde olmalıdır. Bu kuralı görmezden gelmek, eski kapanımlarla (stale closure) ilgili hataların kaynağıdır.
useContext, bağlam değerlerini okumak için bir hook’tur. React.createContext tarafından oluşturulan bir bağlam nesnesini alır ve geçerli değeri döndürür. Bağlam değişirse, bileşen otomatik olarak yeni değerle yeniden render edilir.
useContext’ten önce, işlevsel bileşenlerde bağlama erişmek için Context.Consumer ile sarmalamak gerekiyordu, bu da iç içelik yaratıyordu. useContext, Consumer’ı tamamen ortadan kaldırır — işlevsel bileşende herhangi bir yerde hook’u çağırmanız yeterlidir.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('açık');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
useContext, Redux veya küresel durumun yerine geçmez. React, bağlamı nadiren değişen değerler için kullanmayı önerir — tema, yerel ayar, kimlik doğrulama durumu. Sık güncellenen veriler için, her yeniden render tüm bağlam tüketicilerini yeniden hesaplar ve bu da performansı düşürebilir.
useReducer, karmaşık güncelleme mantığına sahip durumlar için useState’e bir alternatiftir. Bir indirgeyici işlevi ve başlangıç durumunu alır, geçerli durumu ve bir dispatch işlevini döndürür. İndirgeyici, geçerli durumu ve bir eylemi alır, yeni bir durum döndürür — yan etkileri olmayan saf bir işlev.
useReducer, birden çok eylem (set, reset, increment, addItem) tek bir durumu yönettiğinde özellikle kullanışlıdır. Farklı setter’larla birden çok useState yerine — net eylem türlerine sahip tek bir indirgeyici. Bu, mantığı öngörülebilir ve kolayca test edilebilir hale getirir.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setStep':
return { ...state, step: action.step };
case 'reset':
return { count: 0, step: 1 };
default:
throw new Error('Unknown action: ' + action.type);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1
});
return (
<>
<p>Count: {state.count}</p>
<button
onClick={() => dispatch({ type: 'increment' })}>+</button>
<button
onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
useRef, render’lar arasında kalıcı olan değiştirilebilir bir nesne oluşturur. current’ı değiştirmek yeniden render’ı tetiklemez. Ana kullanım alanları, DOM öğelerine erişmek ve değiştirilebilir değerleri (zamanlayıcılar, önceki değerler, kütüphane örnekleri) depolamaktır.
useMemo bir işlevin sonucunu belleğe alır. Değeri yalnızca bir bağımlılık değiştiğinde yeniden hesaplar. Pahalı hesaplamalar için kullanın — büyük dizileri filtreleme, sıralama, veri biçimlendirme. useCallback, işlevleri belleğe almak için useMemo’nun özel bir durumudur.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// Mount’ta otomatik odak
useEffect(() => {
inputRef.current?.focus();
}, []);
// Filtrelenmiş sonucu belleğe al
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// Geri aramayı belleğe al
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<div>
<input ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
/>
<ul>
{filtered.map(item =>
<li key={item.id}>
<button
onClick={() => handleSelect(item.id)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
Önemli bir kural: useRef, useMemo ve useCallback optimizasyon araçlarıdır, zorunlu yapılar değil. Her şeyi useMemo ile sarmayın — önce performansı ölçün. Erken optimizasyon kodu karmaşıklaştırır. React bileşenleri hızlı bir şekilde render eder; useMemo yalnızca hesaplamalar gerçekten pahalı olduğunda (O(n²) ve üzeri) gereklidir.
React, hook’lar için iki katı kural getirmiştir. Birincisi: hook’ları yalnızca en üst düzeyde çağırın — koşullar, döngüler veya iç içe işlevler içinde değil. İkincisi: hook’ları yalnızca React işlevsel bileşenlerinden veya özel hook’lardan çağırın. ESLint eklentisi eslint-plugin-react-hooks her iki kuralı da otomatik olarak denetler.
En yaygın hata hook çağrı sırasını ihlal etmektir. React, render’lar arasındaki hook çağrı sırasına güvenir. Bir hook bir koşul içindeyse ve bir render’da çalışmazsa, sonraki tüm hook’lar kayar ve hatalara neden olur.
| Hata | Ne Olur | Çözüm |
|---|---|---|
| Koşulda hook | Hook sırası bozulur | Koşul mantığını hook içine taşıyın |
| Stale closure | Efekt eski değeri kullanır | Tüm bağımlılıkları diziye ekleyin |
| Sonsuz döngü | Bağımlılığı olmayan useEffect durumu değiştirir | Bağımlılıkları belirtin veya useReducer kullanın |
| Render’da ref değişikliği | İşlev gövdesinde yan etki | Mutasyonları useEffect’e taşıyın |
İkinci yaygın hata, useEffect’teki stale closure’dır. Efekt, bağımlılıklarda belirtilmeyen bir değişken kullanırsa, efekt kapanım oluşturma anındaki değerini “yakalar”. Çözüm, kullanılan tüm değişkenleri her zaman bağımlılık dizisine dahil etmektir. Çok fazla bağımlılık varsa, efekti birden çok parçaya bölmeyi düşünün.
Sıkça Sorulan Sorular
React, render’lar arasındaki hook çağrı sırasına güvenir. Bir hook bir koşul içinde çağrılır ve bir render’da çalışmazsa, sonraki tüm hook’lar kayar — durumları karışır. ESLint kuralı hooks/exhaustive-deps bu konuda uyarır.
useLayoutEffect eş zamanlıdır ve tarayıcı değişiklikleri çizmeden önce çalışır. useEffect eş zamansızdır — çizimden sonra. useLayoutEffect’i yalnızca kullanıcı değişiklikleri görmeden önce DOM’u ölçmeniz veya değiştirmeniz gerektiğinde kullanın (popover konumu, kaydırma geri yükleme).
useEffect içinde AbortController kullanın. Bir controller oluşturun, signal’ı fetch’e iletin ve temizleme işlevinde controller.abort() çağrın. Eski tarayıcılar için bir cancelled bayrağı kullanın: return () => { cancelled = true; } setState’ten önce bir kontrol ile.
Özel hook, yerleşik hook’ları kullanan, use önekli bir JavaScript işlevidir. Aynı hook mantığı iki veya daha fazla bileşende tekrarlandığında oluşturun. Örnekler: useDebounce, useLocalStorage, useMediaQuery. Özel hook’lar HOC ve render props’ın yerini alır.
Strict Mode’da React, geliştirme ortamında temizlenmemiş efektlerle ilgili hataları bulmak için bileşeni kasıtlı olarak iki kez mount ve unmount eder. Temizleme doğru yazılmışsa, çift çağrı soruna neden olmaz. Bu, üretim modunda gerçekleşmez.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun