React’te Komponent Yaşam Döngülerini useEffect Hook’u ile Değiştirmek
React, kullanıcı arayüzleri oluşturmak için deklaratif bir yaklaşım sunan güçlü bir kütüphanedir. Gelişiminin ilk yıllarından itibaren, bileşenlerin davranışlarını yönetmek için sınıf tabanlı bileşenler ve bunlarla ilişkili yaşam döngüsü metodları temel bir yapı taşı olmuştur. Ancak, React Hooks’un tanıtılmasıyla birlikte, fonksiyonel bileşenler, sınıf bileşenlerinin sunduğu yeteneklerin çoğunu, hatta daha fazlasını, daha temiz ve daha modüler bir şekilde sağlamaya başlamıştır. Bu devrimin merkezinde yer alan useEffect hook’u, yan etkileri (side effects) yönetmek ve böylece sınıf bileşenlerinin yaşam döngüsü metodlarını etkili bir şekilde değiştirmek için tasarlanmıştır. Bu makalede, useEffect hook’unun ne olduğunu, nasıl çalıştığını ve componentDidMount, componentDidUpdate ve componentWillUnmount gibi geleneksel yaşam döngüsü metodlarının yerini nasıl aldığını detaylı bir şekilde inceleyeceğiz. Ayrıca, useEffect‘in gelişmiş kullanım senaryolarını, sıkça yapılan hataları ve genel olarak fonksiyonel bileşenlere geçişin getirdiği avantajları ele alacağız.
Sınıf Bileşenlerinde Yaşam Döngüsü Metodlarına Genel Bakış
React sınıf bileşenleri, bir bileşenin ömrü boyunca belirli anlarda otomatik olarak çağrılan özel metodlar aracılığıyla yan etkileri (veri getirme, olay dinleyicileri ayarlama, DOM’u doğrudan manipüle etme vb.) yönetir. Bu metodlar, bileşenin “doğduğu”, “güncellendiği” ve “öldüğü” anları temsil eder. En yaygın kullanılan yaşam döngüsü metodları şunlardır:
* componentDidMount(): Bileşen DOM’a ilk kez render edildikten sonra çağrılır. Genellikle ilk veri getirme işlemleri, olay dinleyicileri ayarlama veya DOM üzerinde doğrudan işlem yapma gibi tek seferlik kurulum işlemleri için kullanılır.
* componentDidUpdate(prevProps, prevState): Bileşenin prop’ları veya state’i güncellendiğinde, ilk render hariç, her yeniden render işleminden sonra çağrılır. Bu metod, güncellenen prop’lara veya state’e göre yan etkileri (örneğin, yeni veriler getirme) gerçekleştirmek için idealdir.
* componentWillUnmount(): Bileşen DOM’dan kaldırılmadan hemen önce çağrılır. Bellek sızıntılarını önlemek için componentDidMount içinde ayarlanan olay dinleyicilerini, zamanlayıcıları veya abonelikleri temizlemek için kullanılır.
* shouldComponentUpdate(nextProps, nextState): Performans optimizasyonu için kullanılır. Bileşenin yeniden render edilip edilmeyeceğini kontrol eder. true döndürürse render edilir, false döndürürse edilmez.
* getDerivedStateFromProps(nextProps, prevState): Nadiren kullanılır ve state’in prop’lardan türetilmesi gerektiğinde çağrılır. Bu metod, yan etkileri tetiklemek için değil, state’i senkronize etmek için kullanılır.
Sınıf bileşenlerindeki bu yaşam döngüsü metodları, yan etkileri yönetmek için işlevsel bir yol sunsa da, bazı dezavantajları vardır:
* Mantık Dağınıklığı: Aynı mantığa ait kod parçaları (örneğin, bir abonelik kurma ve temizleme) farklı yaşam döngüsü metodlarına dağılabilir. Bu durum, kodun okunabilirliğini ve bakımını zorlaştırır.
* Tekrar Kullanım Zorluğu: Yan etki mantığını bileşenler arasında yeniden kullanmak zordur. Genellikle HOC’ler (Higher-Order Components) veya Render Props gibi daha karmaşık desenler gerektirir.
* this Bağlama Sorunları: JavaScript’teki this anahtar kelimesinin davranışı, özellikle olay işleyicileriyle birlikte kullanıldığında, kafa karıştırıcı olabilir ve ek bağlama işlemleri gerektirebilir.
React Hooks, özellikle useEffect, bu sorunlara zarif bir çözüm sunarak fonksiyonel bileşenlerin gücünü artırmıştır.
useEffect Hook’unun Temelleri
useEffect hook’u, fonksiyonel bileşenlerde yan etkileri gerçekleştirmek için tasarlanmıştır. “Yan etki” terimi, bir fonksiyonun kendi kapsamı dışındaki bir şeyi değiştirmesi veya etkilemesi anlamına gelir. React bağlamında bu, API çağrıları, DOM manipülasyonları, abonelikler, zamanlayıcılar, olay dinleyicileri gibi işlemleri içerir. useEffect, bir bileşenin render işleminden sonra, DOM güncellendikten sonra çalıştırılması gereken kodu tanımlamanıza olanak tanır.
useEffect‘in temel yapısı şöyledir:
import React, { useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// Yan etki kodu buraya yazılır
return () => {
// Temizleme kodu buraya yazılır (isteğe bağlı)
};
}, [bağımlılıklar]); // Bağımlılık dizisi (isteğe bağlı)
// ... bileşen render mantığı
}
useEffect iki ana argüman alır:
1. setup Fonksiyonu (Geri Çağırma Fonksiyonu): Bu fonksiyon, yan etki mantığını içerir. React, bileşen render edildikten sonra (ve DOM güncellendikten sonra) bu fonksiyonu çağırır.
2. dependencies Dizisi (Bağımlılık Dizisi): Bu isteğe bağlı bir dizidir ve useEffect‘in ne zaman yeniden çalıştırılacağını kontrol eder.
* Boş dizi []: Efekt sadece bileşen ilk kez DOM’a bağlandığında (mount) çalışır ve bir daha çalışmaz. Bu, componentDidMount‘a benzer bir davranış sağlar.
* Dizi belirtilmezse: Efekt, bileşenin her render işleminden sonra çalışır. Bu genellikle istenmeyen bir durumdur ve sonsuz döngülere yol açabilir.
* Değerler içeren bir dizi [dep1, dep2]: Efekt, bileşen ilk kez bağlandığında ve dizideki bağımlılıklardan herhangi biri bir önceki render’a göre değiştiğinde çalışır. Bu, componentDidUpdate‘e benzer bir davranış sağlar.
setup fonksiyonu isteğe bağlı olarak bir temizleme fonksiyonu döndürebilir. Bu temizleme fonksiyonu, bir sonraki efekt çalıştırılmadan önce (eğer bağımlılıklar değişirse) ve bileşen DOM’dan kaldırılmadan önce (unmount) çağrılır. Bu, componentWillUnmount‘a benzer bir davranış sağlar.
useEffect‘i düşünürken, onu sadece yaşam döngüsü metodlarının bir yedeği olarak görmek yerine, “bir bileşenin harici bir sistemle senkronize olmasını sağlayan bir mekanizma” olarak düşünmek daha faydalıdır.
useEffect ile Yaşam Döngüsü Metodlarının Yerine Geçme
Şimdi, useEffect‘in sınıf bileşenlerindeki yaygın yaşam döngüsü metodlarını nasıl değiştirdiğini örneklerle inceleyelim.
componentDidMount’u Simüle Etmek
componentDidMount, bileşen ilk kez DOM’a bağlandığında çalıştırılması gereken tek seferlik kurulum mantığı için kullanılır. useEffect ile bunu başarmak için boş bir bağımlılık dizisi kullanırız:
import React, { useState, useEffect } from 'react'; function DataFetcher() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { // Bu fonksiyon sadece bileşen ilk kez mount edildiğinde çalışır console.log('Bileşen mount edildi, veri çekiliyor...'); const fetchData = async () => { try { const response = await fetch('https://api.example.com/data'); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const result = await response.json(); setData(result); } catch (err) { setError(err); } finally { setLoading(false); } }; fetchData(); // Bu durumda bir temizleme işlemi gerekmiyor // return () => { / cleanup code / }; }, []); // Boş bağımlılık dizisi: sadece mount'ta çalışır if (loading) returnYükleniyor...; if (error) returnHata: {error.message}; return (Veri:
{JSON.stringify(data, null, 2)});
}Yukarıdaki örnekte,
useEffectiçindekifetchDatafonksiyonu sadece bileşen ilk kez yüklendiğinde çalışır.[]boş bağımlılık dizisi, React’e bu efektin herhangi bir prop veya state değişikliğinde yeniden çalıştırılmasına gerek olmadığını söyler.componentDidUpdate’i Simüle Etmek
componentDidUpdate, bileşenin prop’ları veya state’i değiştiğinde belirli yan etkileri tetiklemek için kullanılır.useEffectile bu davranışı elde etmek için, bağımlılık dizisine izlemek istediğimiz prop veya state değişkenlerini ekleriz.import React, { useState, useEffect } from 'react'; function UserProfile({ userId }) { const [user, setUser] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { // userId değiştiğinde veya bileşen ilk mount edildiğinde çalışır console.log(userId değişti veya mount edildi: ${userId}, kullanıcı verisi çekiliyor...); setLoading(true); setError(null); // Önceki hataları temizle const fetchUser = async () => { try { const response = await fetch(https://api.example.com/users/${userId}); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const result = await response.json(); setUser(result); } catch (err) { setError(err); } finally { setLoading(false); } }; if (userId) { // userId null veya undefined değilse fetch yap fetchUser(); } else { setUser(null); setLoading(false); } }, [userId]); // userId bağımlılığı: userId değiştiğinde yeniden çalışır if (loading) returnKullanıcı bilgileri yükleniyor...; if (error) returnHata: {error.message}; if (!user) returnKullanıcı seçilmedi veya bulunamadı.; return (); }Kullanıcı Profili: {user.name}
Email: {user.email}
Şehir: {user.address?.city}
Bu örnekte,
userIdprop’u değiştiğinde (veya bileşen ilk kez mount edildiğinde)useEffectiçindeki veri getirme işlemi yeniden tetiklenir.[userId]bağımlılık dizisi, React’euserId‘nin bir önceki render’a göre farklı olup olmadığını kontrol etmesini söyler. Eğer farklıysa, efekt yeniden çalışır.Sadece Güncellemede Çalıştırmak (İlk Mount’ta Değil):
Bazı durumlarda, bir efekti sadece güncellemelerde çalıştırmak, ilk mount’ta çalıştırmamak isteyebilirsiniz.useEffectdoğrudan bu yeteneği sağlamaz çünkü bağımlılık dizisi, ilk mount’ta da çalışmayı garanti eder. Ancak, birrefkullanarak bu davranışı simüle edebilirsiniz:import React, { useState, useEffect, useRef } from 'react'; function MyComponentWithUpdateEffect() { const [count, setCount] = useState(0); const isInitialMount = useRef(true); useEffect(() => { if (isInitialMount.current) { isInitialMount.current = false; } else { // Bu kod sadece güncellemelerde çalışır, ilk mount'ta değil console.log('count güncellendi:', count); document.title =Sayım: ${count}; } }, [count]); // count değiştiğinde çalışır return (); }Sayım: {count}
Ancak, React ekibi genellikle
useEffect‘i bu şekilde kullanmaktan kaçınmayı ve efektlerin hem mount hem de update’te çalışacak şekilde tasarlanmasını önerir. Çoğu durumda, bir efektin hem mount hem de ilgili bağımlılıklar değiştiğinde çalışması doğru davranıştır.componentWillUnmount’u Simüle Etmek
componentWillUnmount, bileşen DOM’dan kaldırılmadan önce temizleme işlemleri (abonelikleri iptal etme, zamanlayıcıları durdurma, olay dinleyicilerini kaldırma) için kullanılır.useEffect‘te, bu temizleme mantığını efekt fonksiyonundan döndürülen bir fonksiyona yerleştiririz.import React, { useState, useEffect } from 'react'; function TimerComponent() { const [seconds, setSeconds] = useState(0); useEffect(() => { console.log('TimerComponent mount edildi, zamanlayıcı başlatılıyor.'); const intervalId = setInterval(() => { setSeconds(prevSeconds => prevSeconds + 1); }, 1000); // Temizleme fonksiyonu return () => { console.log('TimerComponent unmount ediliyor veya efekt yeniden çalışıyor, zamanlayıcı temizleniyor.'); clearInterval(intervalId); // Zamanlayıcıyı durdur }; }, []); // Boş bağımlılık dizisi: sadece mount'ta çalışır ve unmount'ta temizlenir return (); } function App() { const [showTimer, setShowTimer] = useState(true); return (Geçen süre: {seconds} saniye
{showTimer &&); }} Bu örnekte,
TimerComponentmount edildiğinde birsetIntervalbaşlatılır.useEffect‘ten döndürülen temizleme fonksiyonu, bileşen unmount edildiğinde (yaniAppbileşenindekishowTimerfalseolduğunda)clearIntervalçağırarak zamanlayıcının durdurulmasını sağlar. Bu, bellek sızıntılarını ve istenmeyen davranışları önler. Temizleme fonksiyonu ayrıca, bağımlılıklar değiştiğinde ve efekt yeniden çalıştırılmadan önce de çağrılır.componentDidMount ve componentDidUpdate Birlikte
Çoğu zaman, bir efektin hem ilk render’da hem de belirli bağımlılıklar değiştiğinde çalışmasını isteriz. Bu,
useEffect‘in en yaygın kullanım şeklidir vecomponentDidMountilecomponentDidUpdate‘in birleşimini temsil eder.import React, { useState, useEffect } from 'react'; function DocumentTitleUpdater({ titleSuffix }) { const [count, setCount] = useState(0); useEffect(() => { // Hem mount'ta hem de count veya titleSuffix değiştiğinde çalışır document.title =Sayım: ${count} | ${titleSuffix}; console.log(Belge başlığı güncellendi: Sayım: ${count} | ${titleSuffix}); // Bu durumda temizleme fonksiyonu gerekmiyor }, [count, titleSuffix]); // count veya titleSuffix değiştiğinde yeniden çalışır return (); } function App() { const [appTitle, setAppTitle] = useState('React Uygulaması'); return (Sayım: {count}
setAppTitle(e.target.value)} placeholder="Uygulama Başlığını Girin" />); }Burada,
DocumentTitleUpdaterbileşeni mount edildiğinde vecountstate’i veyatitleSuffixprop’u değiştiğindedocument.titlegüncellenir. Bu tekuseEffectçağrısı, sınıf bileşenlerindecomponentDidMountvecomponentDidUpdate‘i ayrı ayrı ele almak yerine, ilgili mantığı tek bir yerde toplar.useEffect’in Gelişmiş Kullanımları ve İpuçları
useEffecthook’u, temel yaşam döngüsü simülasyonlarının ötesinde birçok güçlü özelliğe sahiptir.Bağımlılık Dizisi (Dependency Array) ve Dikkat Edilmesi Gerekenler
Bağımlılık dizisi,
useEffect‘in en önemli ve bazen en kafa karıştırıcı parçasıdır. Doğru kullanımı, performans sorunlarını, sonsuz döngüleri ve beklenmedik davranışları önlemek için kritik öneme sahiptir.* Eksik Bağımlılıklar: Eğer bir efekt içinde kullanılan bir değişken (prop, state, fonksiyon) bağımlılık dizisine dahil edilmezse, efekt eski (stale) bir değere sahip bir closure yakalayabilir. Bu, “stale closure” problemi olarak bilinir. React’in ESLint eklentisi (
eslint-plugin-react-hooks), eksik bağımlılıkları otomatik olarak algılayarak sizi uyarır. Bu uyarıları ciddiye almak önemlidir.
* Fonksiyonları Bağımlılık Olarak Ekleme: Bir efekt içinde tanımlanan veya dışarıdan gelen bir fonksiyon kullanılıyorsa, bu fonksiyon genellikle bağımlılık dizisine eklenmelidir. Ancak, fonksiyon her render’da yeniden oluşturuluyorsa, bu durum efektin gereksiz yere sık sık çalışmasına neden olabilir. Bu tür durumlar içinuseCallbackhook’u kullanılır.useCallback, bir fonksiyonu bağımlılıkları değişmediği sürece hafızaya alır (memoize eder).
* Nesneleri Bağımlılık Olarak Ekleme: Benzer şekilde, bir nesne veya dizi her render’da yeniden oluşturuluyorsa (örneğin,{ id: 1 }veya[1, 2]), bu nesneyi bağımlılık olarak eklemek her render’da efektin çalışmasına neden olur. Bu durumdauseMemohook’u kullanılarak nesne hafızaya alınabilir.import React, { useState, useEffect, useCallback, useMemo } from 'react'; function MyOptimizedComponent({ onSave }) { const [text, setText] = useState(''); // onSave fonksiyonunu hafızaya al, sadece bağımlılıkları değiştiğinde yeniden oluştur const memoizedOnSave = useCallback(() => { onSave(text); }, [onSave, text]); // onSave veya text değiştiğinde yeniden oluştur // Bir nesneyi hafızaya al const config = useMemo(() => ({ apiUrl: '/api/data', timeout: 5000 }), []); // Sadece ilk render'da oluştur useEffect(() => { console.log('Text veya onSave değişti, bir işlem yapılıyor...'); // memoizedOnSave'i kullan // config'i kullan }, [text, memoizedOnSave, config]); // Bağımlılıklar return (setText(e.target.value)} />); }Temizleme Fonksiyonu (Cleanup Function) Detayları
Temizleme fonksiyonu,
useEffect‘in kritik bir parçasıdır ve bellek sızıntılarını, hataları ve istenmeyen yan etkileri önlemek için kullanılır.* Ne Zaman Çalışır?
* Bileşen unmount edildiğinde.
* Efekt yeniden çalıştırılmadan önce (yani, bağımlılıklar değiştiğinde ve yeni bir efekt kurulumu yapılmadan önce).
* Neden Önemli?
* Bellek Sızıntıları: Abonelikler, zamanlayıcılar veya olay dinleyicileri gibi kaynaklar temizlenmezse, bileşen DOM’dan kaldırılsa bile arka planda çalışmaya devam edebilir ve bellek tüketebilir.
* Tutarsız Durum: Eski aboneliklerin veya zamanlayıcıların yanlışlıkla güncel bileşen durumuyla etkileşime girmesi tutarsız durumlara yol açabilir.
* Race Conditions: Özellikle veri getirme işlemlerinde, kullanıcı bir isteği iptal edip yenisini tetiklediğinde, eski isteğin cevabı yeni bir state’i yanlışlıkla güncelleyebilir. Temizleme fonksiyonu, bekleyen istekleri iptal ederek bu durumu önleyebilir.Birden Fazla useEffect Kullanımı
Sınıf bileşenlerinde tüm
componentDidMountmantığı tek bir yerde toplanırken,useEffectile yan etkileri mantıksal olarak ilgili parçalara ayırabiliriz. Bu, kodun daha düzenli, okunabilir ve yönetilebilir olmasını sağlar.import React, { useState, useEffect } from 'react'; function UserDashboard({ userId }) { const [userData, setUserData] = useState(null); const [posts, setPosts] = useState([]); const [loadingUser, setLoadingUser] = useState(true); const [loadingPosts, setLoadingPosts] = useState(true); // Kullanıcı verilerini getirme efekti useEffect(() => { if (!userId) return; setLoadingUser(true); fetch(/api/users/${userId}) .then(res => res.json()) .then(data => setUserData(data)) .catch(error => console.error("Kullanıcı verisi hatası:", error)) .finally(() => setLoadingUser(false)); }, [userId]); // userId değiştiğinde çalışır // Kullanıcının gönderilerini getirme efekti useEffect(() => { if (!userId) return; setLoadingPosts(true); fetch(/api/users/${userId}/posts) .then(res => res.json()) .then(data => setPosts(data)) .catch(error => console.error("Gönderi verisi hatası:", error)) .finally(() => setLoadingPosts(false)); }, [userId]); // userId değiştiğinde çalışır // Belge başlığını güncelleme efekti useEffect(() => { document.title = userData ?${userData.name} Paneli: 'Kullanıcı Paneli'; }, [userData]); // userData değiştiğinde çalışır if (loadingUser || loadingPosts) returnYükleniyor...; return (); }{userData?.name} Paneli
Email: {userData?.email}
Gönderiler:
{posts.map(post => (
- {post.title}
))}Bu örnekte, üç farklı
useEffectçağrısı, her biri kendi mantıksal yan etkisini yönetir: kullanıcı verilerini getirme, gönderileri getirme ve belge başlığını güncelleme. Her biri kendi bağımlılıklarına sahiptir ve bu, kodun daha anlaşılır olmasını sağlar.Sonsuz Döngüleri Önleme
useEffectiçinde state’i güncellerken dikkatli olunmalıdır, aksi takdirde sonsuz döngülere yol açabilir. Eğer biruseEffectiçindeuseStateile bir state güncelleniyor ve bu state bağımlılık dizisinde yer alıyorsa, efekt her çalıştığında state’i güncelleyecek, bu da efektin tekrar çalışmasına neden olacak ve sonsuz bir döngü oluşacaktır.// Yanlış kullanım - Sonsuz döngü! function BadCounter() { const [count, setCount] = useState(0); useEffect(() => { // count değiştiğinde bu efekt çalışır // setCount(count + 1) çağrılır, bu da count'ı günceller // count güncellendiği için efekt tekrar çalışır... sonsuz döngü! setCount(count + 1); }, [count]); returnSayım: {count}
; }Bu tür döngüleri önlemek için:
* State güncellemelerini bağımlılık dizisine dahil etmeniz gerekiyorsa, genelliklesetCount(prevCount => prevCount + 1)gibi bir fonksiyonel güncelleme kullanmak veyauseReducergibi daha karmaşık state yönetim çözümlerine yönelmek daha güvenlidir.
* En önemlisi,useEffect‘in ne zaman çalışması gerektiğini dikkatlice düşünün ve bağımlılık dizisini doğru ayarlayın. Eğer bir state güncellenmesi efektin kendisi tarafından tetikleniyorsa ve bu güncellemenin efektin yeniden çalışmasına neden olmasını istemiyorsanız, o state’i bağımlılık dizisinden çıkarmaya çalışın (ancak bu genellikleeslint-plugin-react-hooksuyarısı almanıza neden olur veuseRefveyauseCallbackgibi çözümler gerektirebilir).Veri Getirme (Data Fetching) ile useEffect
useEffectile veri getirme, React uygulamalarında çok yaygın bir senaryodur.async/awaitkullanımı,useEffectiçinde doğrudan yapılamaz çünküuseEffect‘in geri çağırma fonksiyonuasyncolamaz (çünkü bir promise döndürür ve React’in beklediği temizleme fonksiyonunu döndürmez). Bunun yerine,asyncfonksiyonu efekt içinde tanımlayıp hemen çağırmak (IIFE – Immediately Invoked Function Expression) veya ayrı birasyncyardımcı fonksiyonu tanımlamak gerekir:import React, { useState, useEffect } from 'react'; function UserList() { const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let isMounted = true; // Temizleme için bayrak const fetchUsers = async () => { try { setLoading(true); const response = await fetch('https://jsonplaceholder.typicode.com/users'); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const data = await response.json(); if (isMounted) { // Bileşen hala mount edilmişse state'i güncelle setUsers(data); } } catch (err) { if (isMounted) { setError(err); } } finally { if (isMounted) { setLoading(false); } } }; fetchUsers(); return () => { isMounted = false; // Bileşen unmount edildiğinde veya efekt yeniden çalışmadan önce bayrağı false yap }; }, []); // Sadece mount'ta çalışır if (loading) returnKullanıcılar yükleniyor...; if (error) returnHata: {error.message}; return (); }Kullanıcılar:
{users.map(user => (
- {user.name} ({user.email})
))}Yukarıdaki örnekte
isMountedbayrağı, bileşen unmount edilmişse state güncellemelerini önleyerek “Cannot update a component (${componentName}) while rendering a different component (${otherComponentName}).” gibi uyarıları veya bellek sızıntılarını engeller. Modern React’te,useEffect‘in temizleme fonksiyonu bu tür race condition’ları yönetmek için daha sofistike yollar sunar, örneğinAbortControllerkullanarak bekleyen fetch isteklerini iptal etmek.// AbortController ile veri getirme temizliği useEffect(() => { const abortController = new AbortController(); const signal = abortController.signal; const fetchUsers = async () => { try { setLoading(true); const response = await fetch('https://jsonplaceholder.typicode.com/users', { signal }); if (!response.ok) { throw new Error(HTTP error! status: ${response.status}); } const data = await response.json(); setUsers(data); } catch (err) { if (err.name === 'AbortError') { console.log('Fetch isteği iptal edildi.'); } else { setError(err); } } finally { setLoading(false); } }; fetchUsers(); return () => { abortController.abort(); // Bileşen unmount edildiğinde veya efekt yeniden çalışmadan önce isteği iptal et }; }, []);Bu yöntem, özellikle birden fazla API çağrısının arka arkaya tetiklendiği durumlarda (örneğin bir arama çubuğuna yazarken) race condition’ları önlemek için çok daha sağlamdır.
Custom Hooks ve useEffect
useEffect‘in belki de en büyük gücü, yan etki mantığını özel hook’lar (custom hooks) aracılığıyla soyutlama ve yeniden kullanma yeteneğidir. Bu, bileşenler arasında durumsuz mantığı paylaşmanın ve karmaşık yan etkileri basitleştirmenin mükemmel bir yoludur.// useDocumentTitle.js import { useEffect } from 'react'; function useDocumentTitle(title) { useEffect(() => { const originalTitle = document.title; document.title = title; return () => { document.title = originalTitle; // Temizleme: orijinal başlığı geri yükle }; }, [title]); // title değiştiğinde çalışır } // MyComponent.js import React, { useState } from 'react'; import useDocumentTitle from './useDocumentTitle'; function MyComponent() { const [name, setName] = useState(''); useDocumentTitle(name ?Merhaba ${name}: 'İsimsiz'); return (setName(e.target.value)} placeholder="Adınızı Girin" />); }Merhaba, {name || 'misafir'}!
useDocumentTitlegibi özel hook’lar, karmaşıkuseEffectmantığını kapsüller ve birden fazla bileşen tarafından kolayca kullanılabilir hale getirir. Bu, kod tekrarını azaltır ve bileşenlerin daha temiz ve odaklanmış olmasını sağlar.Sınıf Bileşenlerinden Fonksiyonel Bileşenlere Geçişin Avantajları
useEffectve diğer hook’ların benimsenmesiyle birlikte, fonksiyonel bileşenler React geliştirmesinde baskın paradigm haline gelmiştir. Bu geçişin birçok önemli avantajı vardır:* Daha Temiz ve Okunabilir Kod:
useEffect, ilgili yan etki mantığını tek bir yerde toplar, bu da kodun okunabilirliğini artırır. Sınıf bileşenlerinde aynı mantığın farklı yaşam döngüsü metodlarına dağılması sorunu ortadan kalkar.
* Daha Kolay Yeniden Kullanım: Özel hook’lar aracılığıyla, durum bilgisi olan mantığı (stateful logic) bileşenler arasında kolayca yeniden kullanabiliriz. Bu, HOC’ler ve Render Props gibi daha karmaşık desenlere olan ihtiyacı azaltır.
* Daha Az Boilerplate:thisanahtar kelimesinin bağlama sorunları, constructor’lar,super()çağrıları gibi sınıf bileşenlerinin getirdiği boilerplate kod ortadan kalkar.
* Daha İyi Performans Optimizasyonu:React.memo,useCallbackveuseMemogibi hook’lar, fonksiyonel bileşenlerde performans optimizasyonunu daha granüler ve kontrol edilebilir bir şekilde yapma imkanı sunar.
* Daha Kolay Test Edilebilirlik: Fonksiyonel bileşenler ve hook’lar, saf fonksiyonlara daha yakın olduğu için test edilmesi genellikle daha kolaydır.
* Gelişmiş Geliştirici Deneyimi: Hooks, React’in geleceğidir ve topluluk tarafından yoğun bir şekilde desteklenmektedir. Yeni özellikler ve iyileştirmeler genellikle önce hook’lar için geliştirilir.Sıkça Yapılan Hatalar ve Çözümleri
useEffectgüçlü bir araç olsa da, yanlış kullanımı yaygın hatalara yol açabilir:1. Yanlış Bağımlılık Dizisi:
* Eksik Bağımlılıklar: Efekt içinde kullanılan bir değişkeni bağımlılık dizisine eklememek, eski değerlerle çalışmaya (stale closures) neden olabilir. Çözüm: ESLint uyarılarını dinleyin ve tüm bağımlılıkları ekleyin. GerekirseuseCallbackveyauseMemokullanın.
* Gereksiz Bağımlılıklar: Efektin gereksiz yere sık çalışmasına neden olabilir. Çözüm: Sadece efektin çalışmasını tetiklemesi gereken değişkenleri ekleyin.
2. Temizleme Fonksiyonunu Unutmak: Özellikle abonelikler, zamanlayıcılar veya olay dinleyicileri gibi kaynaklar temizlenmezse bellek sızıntılarına yol açar. Çözüm: Her zaman bir yan etki kurduğunuzda, onu nasıl temizleyeceğinizi düşünün ve uygun bir temizleme fonksiyonu döndürün.
3. Asenkron İşlemlerin Yanlış Yönetimi: Özellikle veri getirme işlemlerinde, bileşen unmount edildikten sonra state’i güncellemeye çalışmak hatalara yol açabilir (race conditions). Çözüm:AbortControllerveyaisMountedbayrağı gibi mekanizmalarla bekleyen asenkron işlemleri temizleyin.
4.useEffect‘i Yanlış Anlamak:useEffect‘i sadececomponentDidMountveyacomponentDidUpdateolarak düşünmek yerine, “harici bir sistemle senkronizasyon” olarak düşünmek, daha doğru ve esnek bir kullanım sağlar.Sonuç
useEffecthook’u, React’te fonksiyonel bileşenlerin gücünü ve esnekliğini devrim niteliğinde artırmıştır. Sınıf bileşenlerinin yaşam döngüsü metodlarının yerini alarak, yan etkileri yönetme şeklimizi kökten değiştirmiştir.useEffectsayesinde, ilgili mantığı tek bir yerde toplayabilir, kodu daha okunabilir hale getirebilir ve özel hook’lar aracılığıyla kolayca yeniden kullanabiliriz.Her ne kadar başlangıçta bağımlılık dizisi, temizleme fonksiyonları ve asenkron işlemlerin yönetimi gibi konular bir öğrenme eğrisi sunsa da,
useEffect‘in sunduğu avantajlar bu çabaya değer. Modern React uygulamalarında fonksiyonel bileşenler ve hook’lar standart haline gelmiştir veuseEffect‘i ustaca kullanmak, daha sağlam, bakımı daha kolay ve performanslı React uygulamaları geliştirmek için temel bir beceridir. Artık React geliştiricileri, bileşen yaşam döngüsü metodlarının karmaşık dünyasında gezinmek yerine, bileşenlerinin harici sistemlerle nasıl senkronize olması gerektiğine odaklanabilirler. Bu, React’in deklaratif doğasına daha uygun ve genel olarak daha keyifli bir geliştirme deneyimi sunar.