Takip et

React’te Komponent Yaşam Döngülerini useEffect Hook’u ile Değiştirmek

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

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) return 
Yükleniyor...
; if (error) return
Hata: {error.message}
; return (

Veri:

{JSON.stringify(data, null, 2)}

);
}

Yukarıdaki örnekte, useEffect içindeki fetchData fonksiyonu 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. useEffect ile 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) return 
Kullanıcı bilgileri yükleniyor...
; if (error) return
Hata: {error.message}
; if (!user) return
Kullanıcı seçilmedi veya bulunamadı.
; return (

Kullanıcı Profili: {user.name}

Email: {user.email}

Şehir: {user.address?.city}

); }

Bu örnekte, userId prop’u değiştiğinde (veya bileşen ilk kez mount edildiğinde) useEffect içindeki veri getirme işlemi yeniden tetiklenir. [userId] bağımlılık dizisi, React’e userId‘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. useEffect doğrudan bu yeteneği sağlamaz çünkü bağımlılık dizisi, ilk mount’ta da çalışmayı garanti eder. Ancak, bir ref kullanarak 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 (
    

Geçen süre: {seconds} saniye

); } function App() { const [showTimer, setShowTimer] = useState(true); return (
{showTimer && }
); }

Bu örnekte, TimerComponent mount edildiğinde bir setInterval başlatılır. useEffect‘ten döndürülen temizleme fonksiyonu, bileşen unmount edildiğinde (yani App bileşenindeki showTimer false olduğ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 ve componentDidMount ile componentDidUpdate‘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 (
    

Sayım: {count}

); } function App() { const [appTitle, setAppTitle] = useState('React Uygulaması'); return (
setAppTitle(e.target.value)} placeholder="Uygulama Başlığını Girin" />
); }

Burada, DocumentTitleUpdater bileşeni mount edildiğinde ve count state’i veya titleSuffix prop’u değiştiğinde document.title güncellenir. Bu tek useEffect çağrısı, sınıf bileşenlerinde componentDidMount ve componentDidUpdate‘i ayrı ayrı ele almak yerine, ilgili mantığı tek bir yerde toplar.

useEffect’in Gelişmiş Kullanımları ve İpuçları

useEffect hook’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çin useCallback hook’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 durumda useMemo hook’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 componentDidMount mantığı tek bir yerde toplanırken, useEffect ile 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) return 
Yü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

useEffect içinde state’i güncellerken dikkatli olunmalıdır, aksi takdirde sonsuz döngülere yol açabilir. Eğer bir useEffect içinde useState ile 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]);

  return 

Sayım: {count}

; }

Bu tür döngüleri önlemek için:
* State güncellemelerini bağımlılık dizisine dahil etmeniz gerekiyorsa, genellikle setCount(prevCount => prevCount + 1) gibi bir fonksiyonel güncelleme kullanmak veya useReducer gibi 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 genellikle eslint-plugin-react-hooks uyarısı almanıza neden olur ve useRef veya useCallback gibi çözümler gerektirebilir).

Veri Getirme (Data Fetching) ile useEffect

useEffect ile veri getirme, React uygulamalarında çok yaygın bir senaryodur. async/await kullanımı, useEffect içinde doğrudan yapılamaz çünkü useEffect‘in geri çağırma fonksiyonu async olamaz (çünkü bir promise döndürür ve React’in beklediği temizleme fonksiyonunu döndürmez). Bunun yerine, async fonksiyonu efekt içinde tanımlayıp hemen çağırmak (IIFE – Immediately Invoked Function Expression) veya ayrı bir async yardı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) return 
Kullanıcılar yükleniyor...
; if (error) return
Hata: {error.message}
; return (

Kullanıcılar:

    {users.map(user => (
  • {user.name} ({user.email})
  • ))}
); }

Yukarıdaki örnekte isMounted bayrağı, 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ğin AbortController kullanarak 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'}!

); }

useDocumentTitle gibi özel hook’lar, karmaşık useEffect mantığı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ı

useEffect ve 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: this anahtar 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, useCallback ve useMemo gibi 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

useEffect güç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. Gerekirse useCallback veya useMemo kullanı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: AbortController veya isMounted bayrağı gibi mekanizmalarla bekleyen asenkron işlemleri temizleyin.
4. useEffect‘i Yanlış Anlamak: useEffect‘i sadece componentDidMount veya componentDidUpdate olarak düşünmek yerine, “harici bir sistemle senkronizasyon” olarak düşünmek, daha doğru ve esnek bir kullanım sağlar.

Sonuç

useEffect hook’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. useEffect sayesinde, 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 ve useEffect‘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.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.