Derinlemesine React Hooks: Custom Hooks ile Daha Temiz ve Verimli Kod Yazın

Modern React uygulamaları geliştirdikçe, belirli bileşenler arasında tekrarlanan mantık parçalarıyla karşılaşmak kaçınılmaz hale geliyor. Bir API çağrısı yapmak, yerel depolama (localStorage) ile etkileşim kurmak veya kullanıcı girdilerini geciktirmek gibi işlemler, farklı bileşenlerde benzer kod bloklarının tekrar yazılmasına neden olabilir. Bu durum, kod tekrarını artırır, bakımı zorlaştırır ve projenizin ölçeklenebilirliğini olumsuz etkiler. Benim geliştirme tecrübelerimde, bu tür sorunlarla karşılaştığımda Tasarım Desenleri ve kodun yeniden kullanılabilirliğini artıran yaklaşımlar her zaman kurtarıcım olmuştur.
React'in Hook'ları tanıtmasıyla birlikte, bu tür tekrarlayan mantıkları soyutlamak ve yeniden kullanılabilir parçalar haline getirmek için güçlü bir mekanizma ortaya çıktı: Özel Hook'lar (Custom Hooks). Bu yazıda, React uygulamalarınızda Özel Hook'ların gücünü derinlemesine inceleyecek, kod tekrarını nasıl azaltacağınızı, mantığı nasıl paylaştıracağınızı ve sonuç olarak daha temiz, daha verimli ve daha kolay test edilebilir kod nasıl yazacağınızı pratik örneklerle göstereceğim.
Neden Özel Hook'lar Kullanmalıyız?
React'in temel Hook'ları (useState, useEffect, useContext vb.) bileşenler içinde yerel durum yönetimi, yan etkileri yönetme ve bağlama erişim gibi temel işlevleri sunar. Ancak daha karmaşık veya tekrarlayan mantıkları farklı bileşenlerde kullanmak istediğinizde, Özel Hook'lar devreye girer. Özel Hook'lar, aslında sadece diğer Hook'ları (veya JavaScript fonksiyonlarını) çağıran JavaScript fonksiyonlarıdır. Temel amaçları şunlardır:
- Kod Tekrarını Azaltma: Birden fazla bileşende aynı veya benzer mantığı yeniden yazmak yerine, bu mantığı tek bir Özel Hook içinde toplayabilirsiniz.
- Mantık Paylaşımı: Bileşenler arasında durum bilgisi olmayan (stateless) mantığı değil, durum bilgisi olan (stateful) mantığı paylaşmayı sağlarlar. Örneğin, bir API isteğinin yükleme (loading), hata (error) ve veri (data) durumlarını yöneten bir mantık, bir Özel Hook ile kolayca paylaşılabilir.
- Bileşenleri Daha Temiz Tutma: Bileşenlerinizi sadece kullanıcı arayüzü (UI) render etmeye odaklayarak, karmaşık mantıklarını Özel Hook'lara taşıyabilirsiniz. Bu, bileşenlerinizi daha okunabilir ve bakımı daha kolay hale getirir.
- Test Edilebilirlik: Mantığı bileşenden ayırmak, o mantığı izole bir şekilde test etmeyi kolaylaştırır.

Özel Hook Oluşturma Prensipleri
Bir Özel Hook oluşturmak için birkaç basit kural ve en iyi uygulama vardır:
- `use` Öneki: Her Özel Hook'un adı
useile başlamalıdır (örneğin,useFetch,useDebounce). Bu kural, React'in Hook kural ihlallerini tespit etmesini ve diğer geliştiricilere bunun bir Hook olduğunu belirtmesini sağlar. - Sadece React Fonksiyonlarından Çağırın: Özel Hook'ları yalnızca React fonksiyonlarının (fonksiyonel bileşenler veya diğer Özel Hook'lar) en üst seviyesinde çağırmalısınız. Döngülerin, koşulların veya iç içe fonksiyonların içinde Hook çağırmayın.
- Saf Fonksiyonlar Olabilirler: Özel Hook'larınız genellikle bir input alır ve bir output döndürür. Yan etkilere sahip olabilirler (
useEffectkullanımı gibi), ancak temel olarak belirli bir mantığı kapsayan işlevsel birimler olmalıdırlar.
Pratik Özel Hook Örnekleri
Şimdi, sıkça karşılaşılan senaryolar için bazı pratik Özel Hook örneklerine göz atalım.
1. `useDebounce`: Gecikmeli İşlem Mantığı
Arama kutuları, anlık doğrulama (validation) veya pencere yeniden boyutlandırma olayları gibi durumlarda, bir işlemi belirli bir süre bekledikten sonra tetiklemek isteyebilirsiniz. Bu, performans açısından faydalıdır.
import { useState, useEffect } from 'react';
function useDebounce(value, delay) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
// Value her değiştiğinde bir timeout ayarla
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
// Cleanup fonksiyonu: Bir önceki timeout'u temizle
// Yeni value geldiğinde veya component unmount edildiğinde çalışır
return () => {
clearTimeout(handler);
};
}, [value, delay]); // Value veya delay değiştiğinde yeniden çalıştır
return debouncedValue;
}
// Kullanım örneği:
// import React, { useState } from 'react';
// import useDebounce from './useDebounce'; // Hook'u import ettiğiniz varsayılır
// function SearchInput() {
// const [searchTerm, setSearchTerm] = useState('');
// const debouncedSearchTerm = useDebounce(searchTerm, 500); // 500ms gecikme
// // debouncedSearchTerm değiştiğinde API çağrısı yap
// useEffect(() => {
// if (debouncedSearchTerm) {
// console.log('API çağrısı yapılıyor:', debouncedSearchTerm);
// // Gerçek API çağrısı burada yapılabilir
// }
// }, [debouncedSearchTerm]);
// return (
// <input
// type="text"
// placeholder="Ara..."
// value={searchTerm}
// onChange={(e) => setSearchTerm(e.target.value)}
// />
// );
// }
2. `useLocalStorage`: LocalStorage ile Kolay Etkileşim
Kullanıcı tercihlerini veya sepet bilgilerini yerel depolamada saklamak için sıkça kullanılır. Bu Özel Hook, bu işlemleri basitleştirir ve React state ile senkronize eder.
import { useState, useEffect } from 'react';
function useLocalStorage(key, initialValue) {
// State'i localStorage'dan veya initialValue ile başlat
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
// storedValue her değiştiğinde localStorage'ı güncelle
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(storedValue));
} catch (error) {
console.error(error);
}
}, [key, storedValue]);
return [storedValue, setStoredValue];
}
// Kullanım örneği:
// import React from 'react';
// import useLocalStorage from './useLocalStorage';
// function ThemeSwitcher() {
// const [theme, setTheme] = useLocalStorage('app-theme', 'light');
// const toggleTheme = () => {
// setTheme(theme === 'light' ? 'dark' : 'light');
// };
// return (
// <div>
// <p>Şu anki tema: {theme}</p>
// <button onClick={toggleTheme}>Temayı Değiştir</button>
// </div>
// );
// }

3. `useFetch`: API İsteklerini Yönetme
Bu, perhaps en çok tekrar eden mantıklardan biridir. Bir API çağrısının yükleme durumunu (loading), gelen veriyi (data) ve olası hataları (error) yönetmek için bir Özel Hook yazmak, bileşenlerinizi bu boilerplate koddan kurtarır.
import { useState, useEffect } from 'react';
function useFetch(url, options) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);
try {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`HTTP hata kodu: ${response.status}`);
}
const json = await response.json();
setData(json);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url, JSON.stringify(options)]); // URL veya options değiştiğinde tekrar çağır
return { data, loading, error };
}
// Kullanım örneği:
// import React from 'react';
// import useFetch from './useFetch';
// function UserProfile({ userId }) {
// const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
// if (loading) return <p>Kullanıcı bilgileri yükleniyor...</p>;
// if (error) return <p>Hata: {error.message}</p>;
// return (
// <div>
// <h2>{user.name}</h2>
// <p>E-posta: {user.email}</p>
// </div>
// );
// }
Özel Hook'larla İleri Seviye Senaryolar ve Test Edilebilirlik
Özel Hook'lar sadece basit mantık paylaşımları için değildir. Daha karmaşık senaryolarda da kullanılabilirler:
- Form Yönetimi: Form verilerini, doğrulamasını (validation) ve gönderimini (submission) yöneten bir Özel Hook (örn.
useForm) yazabilirsiniz. - Animasyonlar: Bir DOM elemanının animasyon durumunu kontrol eden bir Hook.
- Tarayıcı API'leri ile Etkileşim: Konum (Geolocation), bildirimler (Notifications), çevrimdışı durumu (
navigator.onLine) gibi tarayıcı API'lerini yöneten Hook'lar. - Global State Yönetimi: Bazı durumlarda Context API ile birlikte kullanarak basit bir global state yönetim sistemi oluşturabilirsiniz. Örneğin, Appexa'nın React entegrasyonu gibi araçlar da temelinde bu tür state yönetim prensiplerini kullanır.
Test Edilebilirlik
Özel Hook'lar, mantığı bileşenlerden ayırdığı için çok daha kolay test edilebilir hale gelir. @testing-library/react-hooks gibi kütüphaneler, bir bileşeni render etmeden sadece hook'u test etmenize olanak tanır. Bu, UI'dan bağımsız mantık testi yapmanızı sağlar ve testlerinizi daha hızlı ve güvenilir hale getirir.

Sonuç
Özel Hook'lar, modern React geliştirme pratiğinin vazgeçilmez bir parçasıdır. Bileşenlerinizde kod tekrarını azaltarak, karmaşık mantıkları yeniden kullanılabilir ve test edilebilir birimlere dönüştürerek, uygulamalarınızın kalitesini ve sürdürülebilirliğini önemli ölçüde artırırlar. Bu yazıdaki useDebounce, useLocalStorage ve useFetch gibi örnekler, Özel Hook'ların potansiyelini anlamanız için sadece bir başlangıç noktasıdır.
Uygulamalarınızı geliştirirken, kendinizi aynı kodu tekrar tekrar yazarken bulduğunuzda veya bir bileşenin çok fazla mantık içerdiğini fark ettiğinizde, durup bir Özel Hook yazmayı düşünün. Bu küçük yatırımlar, uzun vadede size büyük faydalar sağlayacak ve ölçeklenebilir sistemler oluşturmanıza yardımcı olacaktır.
Eğer aklınıza takılan sorular olursa veya bu konular hakkında daha fazla bilgi almak isterseniz, bana ismailyagci371@gmail.com adresinden veya sosyal medya kanallarından (İsmail YAĞCI) ulaşabilirsiniz. Sağlıklı ve başarılı kodlamalar dilerim!
Orijinal yazı: https://ismailyagci.com/articles/derinlemesine-react-hooks-custom-hooks-ile-daha-temiz-ve-verimli-kod-yazin
Yorumlar
Yorum Gönder