Custom hook'lar ile React uygulamalarınızda tekrar kullanılabilir mantığın gücünü ortaya çıkarın. Daha temiz ve bakımı kolay kodlar için custom hook'ları nasıl oluşturacağınızı ve kullanacağınızı öğrenin.
Custom Hook'lar: React'te Tekrar Kullanılabilir Mantık Desenleri
React Hook'ları, fonksiyonel bileşenlere state ve yaşam döngüsü özelliklerini getirerek React bileşenleri yazma şeklimizde devrim yarattı. Sundukları birçok avantaj arasında, custom hook'lar birden fazla bileşen arasında mantığı çıkarmak ve yeniden kullanmak için güçlü bir mekanizma olarak öne çıkıyor. Bu blog yazısı, custom hook'ların dünyasına derinlemesine dalacak, faydalarını, oluşturulmalarını ve pratik örneklerle kullanımlarını keşfedecektir.
Custom Hook'lar Nedir?
Özünde, bir custom hook, "use" kelimesiyle başlayan ve diğer hook'ları çağırabilen bir JavaScript fonksiyonudur. Bileşen mantığını yeniden kullanılabilir fonksiyonlara çıkarmanıza olanak tanır. Bu, render props, higher-order components (HOC) veya diğer karmaşık desenlere başvurmadan durum bilgisi olan (stateful) mantığı, yan etkileri (side effects) veya diğer karmaşık davranışları bileşenler arasında paylaşmanın güçlü bir yoludur.
Custom Hook'ların Temel Özellikleri:
- İsimlendirme Kuralı: Custom hook'lar "use" kelimesiyle başlamalıdır. Bu, React'e fonksiyonun hook'lar içerdiğini ve hook kurallarına uyması gerektiğini bildirir.
- Tekrar Kullanılabilirlik: Birincil amaç, yeniden kullanılabilir mantığı kapsülleyerek işlevselliği bileşenler arasında paylaşmayı kolaylaştırmaktır.
- Durum Bilgisi Olan Mantık (Stateful Logic): Custom hook'lar,
useState
hook'unu kullanarak kendi durumlarını yönetebilir, bu da karmaşık durum bilgisi olan davranışları kapsüllemelerine olanak tanır. - Yan Etkiler (Side Effects): Ayrıca
useEffect
hook'unu kullanarak yan etkiler gerçekleştirebilir, harici API'lerle entegrasyonu, veri çekmeyi ve daha fazlasını mümkün kılarlar. - Birleştirilebilirlik (Composable): Custom hook'lar diğer hook'ları çağırabilir, bu da daha küçük, daha odaklı hook'ları birleştirerek karmaşık mantık oluşturmanıza olanak tanır.
Custom Hook Kullanmanın Faydaları
Custom hook'lar, React geliştirmede birçok önemli avantaj sunar:
- Kodun Tekrar Kullanılabilirliği: En belirgin fayda, mantığı birden fazla bileşen arasında yeniden kullanma yeteneğidir. Bu, kod tekrarını azaltır ve daha DRY (Kendini Tekrar Etme) bir kod tabanını teşvik eder.
- Geliştirilmiş Okunabilirlik: Karmaşık mantığı ayrı custom hook'lara çıkararak, bileşenleriniz daha temiz ve anlaşılması daha kolay hale gelir. Çekirdek bileşen mantığı, kullanıcı arayüzünü (UI) render etmeye odaklı kalır.
- Artırılmış Bakım Kolaylığı: Mantık custom hook'larda kapsüllendiğinde, değişiklikler ve hata düzeltmeleri tek bir yerden uygulanabilir, bu da birden fazla bileşende hata yapma riskini azaltır.
- Test Edilebilirlik: Custom hook'lar kolayca izole bir şekilde test edilebilir, bu da yeniden kullanılabilir mantığın onları kullanan bileşenlerden bağımsız olarak doğru çalıştığından emin olmayı sağlar.
- Basitleştirilmiş Bileşenler: Custom hook'lar, bileşenleri dağınıklıktan arındırmaya yardımcı olarak onları daha az ayrıntılı ve birincil amaçlarına daha odaklı hale getirir.
İlk Custom Hook'unuzu Oluşturma
Bir custom hook oluşturmayı pratik bir örnekle gösterelim: pencere boyutunu takip eden bir hook.
Örnek: useWindowSize
Bu hook, tarayıcı penceresinin mevcut genişliğini ve yüksekliğini döndürecektir. Ayrıca pencere yeniden boyutlandırıldığında bu değerleri güncelleyecektir.
import { useState, useEffect } from 'react';
function useWindowSize() {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
height: window.innerHeight,
});
useEffect(() => {
function handleResize() {
setWindowSize({
width: window.innerWidth,
height: window.innerHeight,
});
}
window.addEventListener('resize', handleResize);
// Temizleme sırasında olay dinleyicisini kaldır
return () => window.removeEventListener('resize', handleResize);
}, []); // Boş dizi, effect'in sadece bileşen mount edildiğinde çalışmasını sağlar
return windowSize;
}
export default useWindowSize;
Açıklama:
- Gerekli Hook'ları İçe Aktarma: React'ten
useState
veuseEffect
'i içe aktarıyoruz. - Hook'u Tanımlama: İsimlendirme kuralına uyarak
useWindowSize
adında bir fonksiyon oluşturuyoruz. - State'i Başlatma:
useState
kullanarakwindowSize
durumunu pencerenin başlangıç genişliği ve yüksekliği ile başlatıyoruz. - Olay Dinleyicisini Kurma: Pencereye bir yeniden boyutlandırma olay dinleyicisi eklemek için
useEffect
kullanıyoruz. Pencere yeniden boyutlandırıldığında,handleResize
fonksiyonuwindowSize
durumunu günceller. - Temizleme: Bileşen unmount edildiğinde olay dinleyicisini kaldırmak için
useEffect
'ten bir temizleme fonksiyonu döndürüyoruz. Bu, bellek sızıntılarını önler. - Değerleri Döndürme: Hook, pencerenin mevcut genişliğini ve yüksekliğini içeren
windowSize
nesnesini döndürür.
Custom Hook'u Bir Bileşende Kullanma
Artık custom hook'umuzu oluşturduğumuza göre, onu bir React bileşeninde nasıl kullanacağımızı görelim.
import React from 'react';
import useWindowSize from './useWindowSize';
function MyComponent() {
const { width, height } = useWindowSize();
return (
Pencere genişliği: {width}px
Pencere yüksekliği: {height}px
);
}
export default MyComponent;
Açıklama:
- Hook'u İçe Aktarma:
useWindowSize
custom hook'unu içe aktarıyoruz. - Hook'u Çağırma:
useWindowSize
hook'unu bileşen içinde çağırıyoruz. - Değerlere Erişme: Döndürülen nesneyi parçalayarak (destructure)
width
veheight
değerlerini alıyoruz. - Değerleri Render Etme: Genişlik ve yükseklik değerlerini bileşenin UI'sinde render ediyoruz.
useWindowSize
kullanan herhangi bir bileşen, pencere boyutu değiştiğinde otomatik olarak güncellenecektir.
Daha Karmaşık Örnekler
Custom hook'lar için bazı daha gelişmiş kullanım durumlarını inceleyelim.
Örnek: useLocalStorage
Bu hook, local storage'dan kolayca veri depolamanıza ve almanıza olanak tanır.
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// Değerimizi saklamak için state
// Mantığın sadece bir kez çalışması için useState'e başlangıç değeri verilir
const [storedValue, setStoredValue] = useState(() => {
try {
// Anahtara göre local storage'dan al
const item = window.localStorage.getItem(key);
// Saklanan json'ı parse et veya yoksa başlangıç değerini döndür
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// Hata durumunda da başlangıç değerini döndür
console.log(error);
return initialValue;
}
});
// useState'in setter fonksiyonunun sarmalanmış bir versiyonunu döndür ki bu...
// ... yeni değeri localStorage'a kalıcı olarak kaydeder.
const setValue = (value) => {
try {
// useState ile aynı API'ye sahip olmak için değerin bir fonksiyon olmasına izin ver
const valueToStore = value instanceof Function ? value(storedValue) : value;
// Local storage'a kaydet
window.localStorage.setItem(key, JSON.stringify(valueToStore));
// State'i kaydet
setStoredValue(valueToStore);
} catch (error) {
// Daha gelişmiş bir implementasyon hata durumunu ele alırdı
console.log(error);
}
};
useEffect(() => {
try {
const item = window.localStorage.getItem(key);
setStoredValue(item ? JSON.parse(item) : initialValue);
} catch (error) {
console.log(error);
}
}, [key, initialValue]);
return [storedValue, setValue];
}
export default useLocalStorage;
Kullanım:
import React from 'react';
import useLocalStorage from './useLocalStorage';
function MyComponent() {
const [name, setName] = useLocalStorage('name', 'Guest');
return (
Merhaba, {name}!
setName(e.target.value)}
/>
);
}
export default MyComponent;
Örnek: useFetch
Bu hook, bir API'den veri çekme mantığını kapsüller.
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function fetchData() {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP hatası! durum: ${response.status}`);
}
const json = await response.json();
setData(json);
setLoading(false);
} catch (error) {
setError(error);
setLoading(false);
}
}
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Kullanım:
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading, error } = useFetch('https://jsonplaceholder.typicode.com/todos/1');
if (loading) return Yükleniyor...
;
if (error) return Hata: {error.message}
;
return (
Başlık: {data.title}
Tamamlandı: {data.completed ? 'Evet' : 'Hayır'}
);
}
export default MyComponent;
Custom Hook'lar için En İyi Uygulamalar
Custom hook'larınızın etkili ve sürdürülebilir olmasını sağlamak için şu en iyi uygulamaları izleyin:
- Odaklı Tutun: Her custom hook'un tek ve iyi tanımlanmış bir amacı olmalıdır. Çok fazla şey yapmaya çalışan aşırı karmaşık hook'lar oluşturmaktan kaçının.
- Hook'larınızı Belgeleyin: Her custom hook için amacını, girdilerini ve çıktılarını açıklayan açık ve özlü belgeler sağlayın.
- Hook'larınızı Test Edin: Custom hook'larınızın doğru ve güvenilir bir şekilde çalıştığından emin olmak için birim testleri yazın.
- Açıklayıcı İsimler Kullanın: Custom hook'larınız için amaçlarını açıkça belirten açıklayıcı isimler seçin.
- Hataları Zarif Bir Şekilde Ele Alın: Beklenmedik davranışları önlemek ve bilgilendirici hata mesajları sağlamak için custom hook'larınızda hata yönetimi uygulayın.
- Tekrar Kullanılabilirliği Göz Önünde Bulundurun: Custom hook'larınızı tekrar kullanılabilirlik göz önünde bulundurarak tasarlayın. Onları birden fazla bileşende kullanılabilecek kadar genel yapın.
- Aşırı Soyutlamadan Kaçının: Bir bileşen içinde kolayca halledilebilecek basit mantıklar için custom hook'lar oluşturmayın. Yalnızca gerçekten yeniden kullanılabilir ve karmaşık olan mantığı çıkarın.
Kaçınılması Gereken Yaygın Hatalar
- Hook Kurallarını İhlal Etmek: Hook'ları her zaman custom hook fonksiyonunuzun en üst seviyesinde ve yalnızca React fonksiyon bileşenlerinden veya diğer custom hook'lardan çağırın.
- useEffect'te Bağımlılıkları Göz Ardı Etmek: Eski kapanışları (stale closures) ve beklenmedik davranışları önlemek için
useEffect
hook'unun bağımlılık dizisine gerekli tüm bağımlılıkları eklediğinizden emin olun. - Sonsuz Döngüler Oluşturmak: Bir
useEffect
hook'u içinde state güncellerken dikkatli olun, çünkü bu kolayca sonsuz döngülere yol açabilir. Güncellemenin koşullu olduğundan ve bağımlılıklardaki değişikliklere dayandığından emin olun. - Temizlemeyi Unutmak: Bellek sızıntılarını önlemek için olay dinleyicilerini kaldırmak, abonelikleri iptal etmek ve diğer temizleme görevlerini gerçekleştirmek için
useEffect
'e her zaman bir temizleme fonksiyonu ekleyin.
Gelişmiş Desenler
Custom Hook'ları Birleştirmek
Daha karmaşık mantık oluşturmak için custom hook'lar bir araya getirilebilir. Örneğin, çekilen verileri otomatik olarak local storage'a kaydetmek için bir useLocalStorage
hook'unu bir useFetch
hook'u ile birleştirebilirsiniz.
Hook'lar Arasında Mantık Paylaşımı
Birden fazla custom hook ortak mantığı paylaşıyorsa, bu mantığı ayrı bir yardımcı fonksiyona çıkarabilir ve her iki hook'ta da yeniden kullanabilirsiniz.
Custom Hook'lar ile Context Kullanımı
Custom hook'lar, global duruma erişmek ve onu güncellemek için React Context ile birlikte kullanılabilir. Bu, uygulamanın global durumundan haberdar olan ve onunla etkileşime girebilen yeniden kullanılabilir bileşenler oluşturmanıza olanak tanır.
Gerçek Dünya Örnekleri
İşte custom hook'ların gerçek dünya uygulamalarında nasıl kullanılabileceğine dair bazı örnekler:
- Form Doğrulama: Form durumunu, doğrulamayı ve gönderimi yönetmek için bir
useForm
hook'u oluşturun. - Kimlik Doğrulama: Kullanıcı kimlik doğrulamasını ve yetkilendirmeyi yönetmek için bir
useAuth
hook'u uygulayın. - Tema Yönetimi: Farklı temalar (açık, koyu vb.) arasında geçiş yapmak için bir
useTheme
hook'u geliştirin. - Coğrafi Konum: Kullanıcının mevcut konumunu izlemek için bir
useGeolocation
hook'u oluşturun. - Kaydırma Tespiti: Kullanıcının sayfada belirli bir noktaya kaydırdığını tespit etmek için bir
useScroll
hook'u oluşturun.
Örnek: Haritalama veya teslimat hizmetleri gibi kültürler arası uygulamalar için useGeolocation hook'u
import { useState, useEffect } from 'react';
function useGeolocation() {
const [location, setLocation] = useState({
latitude: null,
longitude: null,
error: null,
});
useEffect(() => {
if (!navigator.geolocation) {
setLocation({
latitude: null,
longitude: null,
error: 'Geolocation bu tarayıcı tarafından desteklenmiyor.',
});
return;
}
const watchId = navigator.geolocation.watchPosition(
(position) => {
setLocation({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
error: null,
});
},
(error) => {
setLocation({
latitude: null,
longitude: null,
error: error.message,
});
}
);
return () => navigator.geolocation.clearWatch(watchId);
}, []);
return location;
}
export default useGeolocation;
Sonuç
Custom hook'lar, daha temiz, daha yeniden kullanılabilir ve daha bakımı kolay React kodu yazmak için güçlü bir araçtır. Karmaşık mantığı custom hook'larda kapsülleyerek bileşenlerinizi basitleştirebilir, kod tekrarını azaltabilir ve uygulamalarınızın genel yapısını iyileştirebilirsiniz. Custom hook'ları benimseyin ve daha sağlam ve ölçeklenebilir React uygulamaları oluşturma potansiyellerini ortaya çıkarın.
Mevcut kod tabanınızda mantığın birden fazla bileşende tekrarlandığı alanları belirleyerek başlayın. Ardından, bu mantığı custom hook'lara yeniden düzenleyin (refactor). Zamanla, geliştirme sürecinizi hızlandıracak ve kodunuzun kalitesini artıracak yeniden kullanılabilir bir hook kütüphanesi oluşturacaksınız.
En iyi uygulamaları takip etmeyi, yaygın hatalardan kaçınmayı ve custom hook'lardan en iyi şekilde yararlanmak için gelişmiş desenleri keşfetmeyi unutmayın. Pratik ve deneyimle, custom hook'ların ustası ve daha etkili bir React geliştiricisi olacaksınız.