Takip et

✨ React Patterns: Her Uzmanın Bilmesi Gereken Tasarım Kalıpları 🧩


✨ React Patterns: Her Uzmanın Bilmesi Gereken Tasarım Kalıpları 🧩🪝

React projelerinizde kod kalabalığından, karmaşıklıktan ve bakım zorluklarından sıkıldınız mı? İşte size modern React uygulamalarında temiz, yeniden kullanılabilir ve sürdürülebilir kod yazmanın altın anahtarı: React tasarım kalıpları (patterns). Bu detaylı makalede, bir React geliştiricisi olarak yolculuğunuzda sizi yeni başlayan seviyesinden bir uzmana taşıyacak, en temelden en ileri düzeye kadar React pattern’larını adım adım keşfedeceğiz. Gerçek dünya senaryoları ve somut kod örnekleriyle, bu kalıpları ne zaman ve nasıl kullanacağınızı öğrenecek, böylece uygulamanızın performansını ve yönetilebilirliğini zirveye taşıyabileceksiniz. Hazır mısınız?

Giriş: Neden React Pattern’ları Bu Kadar Önemli?

React, deklaratif yapısı ve bileşen tabanlı mimarisiyle web geliştiricilerine müthiş bir esneklik sunar. Ancak bu esneklik, beraberinde belirli zorlukları da getirir: Kod tekrarı, bileşenler arası iletişim karmaşası, performans darboğazları ve büyük ölçekli projelerde artan bakım maliyeti. İşte tam da bu noktada, React tasarım kalıpları devreye giriyor. Bu kalıplar, React’in sunduğu olanakları en verimli şekilde kullanarak ortak problemleri çözmek için kabul görmüş, test edilmiş yaklaşımlardır.

Basitçe ifade etmek gerekirse, React pattern’ları, projelerinizi daha modüler, anlaşılır, test edilebilir ve ölçeklenebilir hale getiren düşünce biçimleri ve uygulama stratejileridir. Bir problemi çözmek için yüzlerce farklı yol varken, bu kalıplar size en iyi uygulamaları sunarak tekerleği yeniden icat etme zahmetinden kurtarır. Bu sayede, daha az hata yapar, daha hızlı geliştirir ve takım arkadaşlarınızla daha uyumlu çalışırsınız.

Bu makaledeki temel amacımız, sizi bu kalıplar denizinde yüzdürmek ve her birinin derinliklerine inerek gerçek potansiyellerini ortaya koymak. Öğrenme yolculuğumuza en temelden başlayıp, karmaşık senaryolara uzanan bir yol haritası izleyeceğiz. Her bir bölüm, bir öncekinin üzerine inşa edilerek bilginizi sağlamlaştıracak ve sizi React’in inceliklerine hakim bir uzman haline getirecek. Unutmayın, iyi bir mimar nasıl ki sağlam temeller üzerine dayanıklı yapılar inşa ediyorsa, iyi bir React geliştiricisi de sağlam tasarım kalıplarıyla güçlü uygulamalar inşa eder.

Öğrenme Yol Haritası: React Pattern Yolculuğunuz

React geliştirme serüveninizde, başlangıç seviyesinden uzmanlığa giden yolculuğunuzu daha anlaşılır kılmak için bir yol haritası çizdik. Bu harita, makale boyunca işleyeceğimiz konuları hangi sırayla ele alacağımızı ve her seviyenin ne gibi beceriler kazandıracağını gösterir.

Yeni Başlayanlar İçin Temeller: React Bileşenleri Nasıl Oluşturulur?

React’e yeni adım atanlar için, her şeyin temelinde bileşen (component) kavramı yatar. Bileşenler, React uygulamalarının yapı taşlarıdır ve uygulamanın kullanıcı arayüzünü (UI) oluşturan bağımsız, yeniden kullanılabilir parçalardır. Bu bölümde, bileşenlerin nasıl oluşturulduğunu, props (özellikler) aracılığıyla veri alışverişini ve state (durum) yönetimiyle etkileşimli arayüzler geliştirmeyi öğreneceksiniz. Bu temel bilgiler, sonraki daha karmaşık kalıpları anlamanız için sağlam bir zemin oluşturacaktır.

  • Bileşen Oluşturma: Fonksiyonel ve Sınıf Bileşenleri (güncel projelerde genellikle fonksiyonel bileşenler tercih edilir).
    
    // Fonksiyonel Bileşen Örneği
    import React from 'react';
    
    function HosgeldinMesaji(props) {
      return 

    Merhaba, {props.ad}!

    ; } export default HosgeldinMesaji;


    Bu basit örnek, bir fonksiyonel bileşenin nasıl tanımlandığını ve kendisine dışarıdan verilen ad prop'unu nasıl kullandığını gösterir. Prop'lar, bileşenler arasında veri akışını sağlayan ana mekanizmadır.

  • Props Kullanımı: Bileşenler arası veri aktarımı.
  • State Yönetimi (useState Hook'u ile): Bileşenlerin kendi içinde durum tutması ve güncellenmesi.
    
    // State Örneği (sayıcı)
    import React, { useState } from 'react';
    
    function Sayac() {
      const [sayi, setSayi] = useState(0);
    
      return (
        

    Şu anki sayı: {sayi}

    ); } export default Sayac;


    Yukarıdaki Sayac bileşeni, useState hook'unu kullanarak sayi adında bir durum değişkeni tanımlar. Bu değişken, bileşenin içinde tutulan dinamik veriyi temsil eder ve setSayi fonksiyonu ile güncellendiğinde bileşen yeniden render edilir.

  • Bileşen Kompozisyonu: Küçük bileşenleri birleştirerek daha büyük ve karmaşık bileşenler oluşturma. Bu, React'in en temel ve güçlü prensibidir.
    
    // Bileşen Kompozisyonu Örneği
    import React from 'react';
    
    function Buton({ onClick, metin }) {
      return ;
    }
    
    function Kart({ baslik, icerik, butonMetni, onButonClick }) {
      return (
        

    {baslik}

    {icerik}

    ); } function Uygulama() { return (
    console.log('Başla tıklandı!')} />
    ); } export default Uygulama;


    Burada Kart bileşeni, Buton bileşenini kendi içinde kullanarak daha karmaşık bir arayüz elemanı oluşturur. Bu, React'in "küçük, odaklanmış bileşenler oluştur ve onları birleştir" felsefesinin en güzel örneğidir.

    Uzman İpucu: İyi bir bileşen kompozisyonu, kodunuzu daha okunabilir, daha test edilebilir ve daha az bağımlı hale getirir. Her bileşenin tek bir sorumluluğu olmasına özen gösterin.

Bu temel bilgilerle donandıktan sonra, artık React ekosistemindeki daha ileri seviye pattern'ları anlamaya hazırsınız. Her pattern, belirli bir problemi çözmek için özelleşmiş bir yaklaşımdır ve bu yolculuğun sonunda, projenizin ihtiyaçlarına göre en doğru pattern'ı seçebilen bir uzman olacaksınız.

Orta Düzey Geliştiriciler İçin: Kod Tekrarını Azaltma ve Ayrıştırma Stratejileri

React projeleri büyüdükçe, kod tekrarı ve bileşenler arası sıkı bağımlılık gibi sorunlarla karşılaşmak kaçınılmaz hale gelir. Orta düzey React pattern'ları, bu problemleri çözmek ve uygulamanızın mimarisini daha düzenli hale getirmek için güçlü araçlar sunar. Bu bölümde, Container/Presenter, Context API gibi kalıpları inceleyerek kodunuzu daha modüler ve yönetilebilir hale getirme yollarını öğreneceğiz.

1. Container/Presenter (Akıllı/Aptal Bileşenler) Pattern'ı Nedir?

Bu pattern, bileşenleri iki ana kategoriye ayırır:

  • Container Bileşenleri (Akıllı): Veri ve iş mantığından sorumludur. Genellikle bir Redux store'a, Context API'ye veya bir API'ye bağlanır. Kendi içinde state tutar ve çocuklarına prop olarak veri ve callback fonksiyonları iletir. Genellikle DOM işaretlemesi (markup) içermez, sadece çocuk bileşenlerini render eder.
  • Presenter Bileşenleri (Aptal): Sadece UI render etmekten sorumludur. Prop'ları alır ve bunları doğrudan kullanır, kendi içinde iş mantığı veya veri çekme operasyonları barındırmaz. Genellikle stil ve görünüm odaklıdır. Yeniden kullanılabilirliği yüksektir.

Neden Kullanılır? Bu ayrım, sorumlulukları netleştirir, bileşenlerin yeniden kullanılabilirliğini artırır ve test etmeyi kolaylaştırır.


// Presenter Bileşen Örneği: KullaniciBilgileri.js
// Bu bileşen sadece prop'ları alır ve render eder.
import React from 'react';

function KullaniciBilgileri({ ad, email }) {
  return (
    

Ad: {ad}

Email: {email}

); } export default KullaniciBilgileri;


// Container Bileşen Örneği: KullaniciProfili.js
// Bu bileşen veriyi yönetir ve Presenter bileşene aktarır.
import React, { useState, useEffect } from 'react';
import KullaniciBilgileri from './KullaniciBilgileri'; // Presenter bileşenimizi import ediyoruz

function KullaniciProfili() {
  const [kullanici, setKullanici] = useState(null);
  const [yukleniyor, setYukleniyor] = useState(true);

  useEffect(() => {
    // Gerçek dünya senaryosunda burada bir API çağrısı olurdu
    setTimeout(() => {
      setKullanici({ ad: 'Fatih Soysal', email: 'info@fatihsoysal.com' });
      setYukleniyor(false);
    }, 1500);
  }, []);

  if (yukleniyor) {
    return 
Kullanıcı bilgileri yükleniyor...
; } if (!kullanici) { return
Kullanıcı bulunamadı.
; } return (

Kullanıcı Profili

); } export default KullaniciProfili;

Bu örnekte, KullaniciProfili (Container) veriyi yönetirken, KullaniciBilgileri (Presenter) sadece bu veriyi gösterir. Bu net ayrım, uygulamanın genel yapısını daha anlaşılır kılar.

2. Context API: Prop Drilling Problemine Çözüm Nasıl Getirilir?

Büyük React uygulamalarında, prop'ları derinlemesine iç içe geçmiş bileşen ağacında birden çok seviye aşağıya doğru iletmek zorunda kalmak ("prop drilling" olarak adlandırılır) yaygın bir problemdir. Bu, kodu okunaksız hale getirir, bakımı zorlaştırır ve hatalara yol açabilir. React'in Context API'si, bu sorunu çözmek için tasarlanmıştır.

Context API Nedir? Context, prop'ları her seviyede manuel olarak geçirmeden bileşen ağacındaki verilere erişim sağlamak için bir yol sunar. Genellikle uygulama genelindeki temalar, kullanıcı kimlik doğrulama bilgileri veya dil tercihleri gibi verileri paylaşmak için kullanılır.


// Context Oluşturma: TemaContext.js
import React, { createContext, useState, useContext } from 'react';

// 1. Context'i oluştur
const TemaContext = createContext();

// 2. Provider bileşenini oluştur
export const TemaProvider = ({ children }) => {
  const [tema, setTema] = useState('light'); // Varsayılan tema
  const toggleTema = () => {
    setTema((prevTema) => (prevTema === 'light' ? 'dark' : 'light'));
  };

  return (
    
      {children}
    
  );
};

// 3. Context'i kullanan bir hook oluştur (opsiyonel ama önerilen)
export const useTema = () => {
  return useContext(TemaContext);
};
            


// Uygulama Dosyası (örneğin App.js)
import React from 'react';
import { TemaProvider, useTema } from './TemaContext'; // Provider ve hook'u import et

// Tema değiştiren bir bileşen
function TemaButonu() {
  const { tema, toggleTema } = useTema(); // useTema hook'unu kullanarak context'e erişim
  return (
    
  );
}

// Temayı gösteren bir başka bileşen
function TemaGosterici() {
  const { tema } = useTema();
  return (
    

Mevcut tema: {tema}

); } // Ana Uygulama bileşeni function App() { return (

Context API Örneği

Daha fazla bilgi için Fatih Soysal'ın blogunu inceleyebilirsiniz.

); } export default App;

Bu örnekte, TemaProvider bileşen ağacının üst seviyelerinde tanımlanır ve tema durumu ile toggleTema fonksiyonunu tüm alt bileşenlerine sağlar. TemaButonu ve TemaGosterici gibi herhangi bir alt bileşen, useTema custom hook'u aracılığıyla bu değerlere erişebilir, böylece prop'ları her seviyeden geçirme ihtiyacı ortadan kalkar.

Dikkat: Context API global state yönetim çözümlerinin (Redux gibi) yerini almaz, ancak daha küçük ve spesifik global durumlar için mükemmel bir çözümdür. Aşırı kullanıldığında, uygulamanın performansını olumsuz etkileyebilir çünkü Context değiştiğinde, onu tüketen tüm bileşenler yeniden render edilir.

İleri Düzey Uzmanlar İçin: Performans ve Yeniden Kullanılabilirlik Mucizesi

React'te gerçekten uzmanlaşmak, sadece temel pattern'ları bilmekle kalmaz, aynı zamanda karmaşık problemleri çözmek, performansı optimize etmek ve kodunuzu maksimum düzeyde yeniden kullanılabilir kılmak anlamına gelir. Bu bölüm, en popüler ve güçlü ileri düzey React pattern'larını keşfedecek, her birini detaylı kod örnekleri ve gerçek dünya senaryolarıyla açıklayacağız.

1. Custom Hooks: Stateful Mantığı Nasıl Yeniden Kullanılır?

Hooks, React 16.8 ile hayatımıza girerek fonksiyonel bileşenlerde state ve lifecycle özelliklerini kullanmamızı sağladı. Custom Hooks ise, bu gücü bir adım öteye taşıyarak stateful mantığı bileşenler arasında yeniden kullanılabilir hale getirmemizi sağlar. Tekrar eden iş mantığını izole etmek ve bileşenlerimizi daha temiz tutmak için mükemmel bir yoldur.

Custom Hooks Nedir? "use" kelimesiyle başlayan, diğer hook'ları (useState, useEffect vb.) kullanan ve özel bir mantık içeren JavaScript fonksiyonlarıdır.


// Custom Hook Örneği: useToggle.js
import { useState } from 'react';

function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);

  const toggle = () => {
    setValue(currentValue => !currentValue);
  };

  return [value, toggle];
}

export default useToggle;
            


// Custom Hook'u Kullanma Örneği: AyarPaneli.js
import React from 'react';
import useToggle from './useToggle'; // Custom hook'umuzu import ediyoruz

function AyarPaneli() {
  const [darkModAcikMi, toggleDarkMod] = useToggle(false);
  const [bildirimlerAcikMi, toggleBildirimler] = useToggle(true);

  return (
    

Ayarlar

Koyu Mod: {darkModAcikMi ? 'Açık' : 'Kapalı'}

Bildirimler: {bildirimlerAcikMi ? 'Açık' : 'Kapalı'}

); } export default AyarPaneli;

Bu örnekte, useToggle custom hook'u, herhangi bir boolean değeri açma/kapama mantığını soyutlar. Bu sayede, AyarPaneli bileşeni içindeki mantık daha sade kalır ve useToggle hook'u farklı bileşenlerde tekrar kullanılabilir.

2. Render Props: Esnek Bileşen Oluşturma Sanatı

Render Props, bir bileşenin ne render edeceğini, bir prop olarak aldığı fonksiyona devretmesini sağlayan bir tekniktir. Bu pattern, bileşenler arasında yeniden kullanılabilir mantık paylaşmak için çok güçlü bir yol sunar, özellikle UI bileşenlerinin davranışını değiştirmeden sadece render çıktısını özelleştirmek istediğinizde kullanışlıdır.

Render Props Nedir? Adından da anlaşılacağı gibi, bir bileşene prop olarak bir fonksiyon iletilir ve bu fonksiyon, bileşenin render etmesi gereken içeriği döndürür.


// Render Props Örneği: MouseTakipci.js
import React, { useState } from 'react';

function MouseTakipci(props) {
  const [x, setX] = useState(0);
  const [y, setY] = useState(0);

  const handleMouseMove = (event) => {
    setX(event.clientX);
    setY(event.clientY);
  };

  return (
    
{props.render({ x, y })} {/* render prop'unu çağırıyoruz */}
); } export default MouseTakipci;


// MouseTakipci bileşenini kullanma örneği: Uygulama.js
import React from 'react';
import MouseTakipci from './MouseTakipci';

function App() {
  return (
    

Render Props Örneği

Aşağıdaki kutu üzerinde fareyi hareket ettirin:

(

Fare Konumu: ({x}, {y})

)} /> ( )} />
); } export default App;

MouseTakipci bileşeni, sadece fare pozisyonu mantığını içerir ve bu bilgiyi render prop'u aracılığıyla kendisine iletilen fonksiyona verir. Bu fonksiyon, fare pozisyonuna göre ne render edileceğine karar verir. Böylece, MouseTakipci'nin mantığı farklı UI temsilleriyle tekrar kullanılabilir hale gelir.

3. Compound Components: İlişkili Bileşen Gruplarını Yönetme

Compound Components (Bileşik Bileşenler) pattern'ı, bir grup bileşenin birbiriyle etkileşime girmesi gereken, ancak esnek bir şekilde birlikte kullanılabildiği senaryolar için idealdir. Select/Option, Accordion/Panel gibi bileşen grupları bunun tipik örnekleridir. Bu pattern, bileşenlerin iç state'ini ve mantığını ebeveyn bileşende tutarak, çocuk bileşenlerin bu state'e erişmesini ve onu değiştirmesini sağlar.

Compound Components Nedir? Bu pattern, bir ebeveyn bileşeninin ve birkaç çocuk bileşeninin belirli bir işlevselliği sağlamak için birlikte çalıştığı durumlarda kullanılır. Ebeveyn bileşen, çocukları arasında örtülü bir şekilde durum ve mantık paylaşır.


// Compound Components Örneği: TabPanel.js
import React, { useState, createContext, useContext } from 'react';

// 1. Context'leri oluştur
const TabContext = createContext();

// 2. Ana Tab bileşeni (Provider görevi görür)
function Tabs({ children }) {
  const [activeTab, setActiveTab] = useState(0); // İlk tab aktif
  return (
    
      
{children}
); } // 3. Tab Listesi bileşeni function TabList({ children }) { return
{children}
; } // 4. Tab Düğmesi bileşeni function Tab({ index, children }) { const { activeTab, setActiveTab } = useContext(TabContext); const isActive = activeTab === index; return ( ); } // 5. Tab Paneli bileşeni function TabPanel({ index, children }) { const { activeTab } = useContext(TabContext); return activeTab === index ?
{children}
: null; } // Bileşenleri dışa aktar Tabs.List = TabList; Tabs.Tab = Tab; Tabs.Panel = TabPanel; export default Tabs;


// Compound Components'ı Kullanma Örneği: Uygulama.js
import React from 'react';
import Tabs from './TabPanel'; // Tabs bileşenini import ediyoruz

function App() {
  return (
    

Compound Components Örneği

Hakkımızda Hizmetlerimiz İletişim

Bu şirket 2020 yılında kurulmuştur.

Web geliştirme, mobil uygulama geliştirme ve danışmanlık hizmetleri sunuyoruz.

Bize info@fatihsoysal.com adresinden ulaşabilirsiniz.

); } export default App;

Bu örnekte, Tabs (ebeveyn) bileşeni, activeTab state'ini yönetir ve bunu TabContext aracılığıyla Tabs.Tab ve Tabs.Panel (çocuk) bileşenlerine sağlar. Çocuk bileşenler, bu context'i kullanarak hangi tab'ın aktif olduğunu bilir ve buna göre render olur. Bu yapı, geliştiricilere Tabs bileşenini kullanırken büyük esneklik sağlar, çünkü Tabs.Tab ve Tabs.Panel elemanlarını istedikleri sırayla veya aralarına başka HTML elemanları koyarak kullanabilirler, ancak yine de birbirleriyle senkronize çalışırlar.

4. Higher-Order Components (HOCs): Bileşenleri Geliştirme

Higher-Order Components (HOCs), bir React bileşenini girdi olarak alan ve gelişmiş/modifiye edilmiş yeni bir bileşen döndüren fonksiyonlardır. Bir HOC, stateful mantığı veya prop'ları paylaşmak, belirli bir bileşene ek işlevsellik katmak veya render koşullarını yönetmek için kullanılabilir. React Hooks öncesinde yaygın olarak kullanılan bir pattern'dı ve hala bazı durumlarda geçerliliğini korumaktadır.

HOC Nedir? Fonksiyonel programlamadaki Higher-Order Functions kavramına benzer şekilde, bileşenleri soyutlama ve yeniden kullanılabilirlik sağlamak için kullanılır.


// HOC Örneği: withVeriYukleme.js
import React, { useState, useEffect } from 'react';

function withVeriYukleme(WrappedComponent, fetchDataUrl) {
  return function WithData(props) {
    const [veri, setVeri] = useState(null);
    const [yukleniyor, setYukleniyor] = useState(true);
    const [hata, setHata] = useState(null);

    useEffect(() => {
      const fetchData = async () => {
        try {
          const response = await fetch(fetchDataUrl);
          if (!response.ok) {
            throw new Error(HTTP Hata! Durum: ${response.status});
          }
          const result = await response.json();
          setVeri(result);
        } catch (error) {
          setHata(error);
        } finally {
          setYukleniyor(false);
        }
      };

      fetchData();
    }, []); // fetchDataUrl değiştiğinde yeniden veri çekmek için buraya ekleyebilirsiniz

    if (yukleniyor) {
      return 
Veriler yükleniyor...
; } if (hata) { return
Hata: {hata.message}
; } return ; }; } export default withVeriYukleme;


// HOC'u Kullanma Örneği: KullaniciListesi.js
import React from 'react';
import withVeriYukleme from './withVeriYukleme';

// Veriyi gösterecek temel bileşen
function KullaniciListesi({ veri, title }) {
  return (
    

{title}

    {veri && veri.map(kullanici => (
  • {kullanici.name} ({kullanici.email})
  • ))}
); } // HOC ile KullaniciListesi bileşenini sarmala const VeriliKullaniciListesi = withVeriYukleme(KullaniciListesi, 'https://jsonplaceholder.typicode.com/users'); // Ana Uygulama bileşeninde kullanma function App() { return (

HOC Örneği

); } export default App;

Bu örnekte, withVeriYukleme bir HOC'tur. KullaniciListesi bileşenini sarmalar ve ona bir veri prop'u enjekte eder. withVeriYukleme fonksiyonu, veriyi çekme, yüklenme durumunu yönetme ve hata yakalama mantığını soyutlar. Bu sayede, KullaniciListesi bileşeni sadece veriyi gösterme sorumluluğunu taşır, veri çekme mantığıyla uğraşmaz.

Dikkat: HOC'lar güçlü olsa da, prop çakışmaları, sarılmış bileşenlerin adlarının kaybolması (React DevTools'ta debugging zorluğu) ve birden fazla HOC'un iç içe kullanılması durumunda oluşan "wrapper hell" gibi dezavantajları olabilir. Bu dezavantajlar nedeniyle, çoğu durumda Custom Hooks daha modern ve tercih edilen bir alternatiftir.

5. Performans Optimizasyonu: React.memo, useCallback ve useMemo ile Nasıl Hız Kazanılır?

Büyük ve karmaşık React uygulamalarında performans darboğazları yaşanması kaçınılmazdır. Gereksiz yere yeniden render edilen bileşenler, uygulamanın yavaşlamasına neden olabilir. React, bu sorunları gidermek için React.memo, useCallback ve useMemo gibi optimizasyon araçları sunar.

  • React.memo (Sadece Fonksiyonel Bileşenler İçin): Bir bileşenin prop'ları değişmediği sürece yeniden render edilmesini engeller. Belleğe alma (memoization) işlemi yapar.
    
    import React from 'react';
    
    // Sadece prop'ları değiştiğinde yeniden render olacak bileşen
    const MemoizedList = React.memo(function MemoizedList({ items }) {
      console.log('MemoizedList render edildi');
      return (
        
      {items.map((item, index) => (
    • {item}
    • ))}
    ); }); export default MemoizedList;

    MemoizedList bileşeni, React.memo ile sarıldığında, items prop'u değişmediği sürece yeniden render edilmeyecektir. Bu, özellikle sık güncellenen üst bileşenlerin altındaki statik listeler için büyük performans artışı sağlar.

  • useCallback: Fonksiyonları belleğe almak (memoize etmek) için kullanılır. Bir fonksiyonu, bağımlılıkları değişmediği sürece yeniden oluşturmaz. Bu, özellikle child bileşenlere prop olarak callback'ler ilettiğinizde ve bu child bileşenler React.memo ile optimize edildiğinde önemlidir.
    
    import React, { useState, useCallback } from 'react';
    
    function ParentComponent() {
      const [count, setCount] = useState(0);
      const [text, setText] = useState('');
    
      // Sadece count değiştiğinde yeniden oluşturulacak bir fonksiyon
      const handleClick = useCallback(() => {
        setCount(c => c + 1);
      }, []); // Bağımlılık dizisi boş olduğu için sadece bir kez oluşturulur
    
      // Her render'da yeniden oluşturulacak bir fonksiyon
      const handleTextChange = (e) => {
        setText(e.target.value);
      };
    
      return (
        

    Sayaç: {count}

    Giriş: {text}

    ); } export default ParentComponent;

    handleClick fonksiyonu useCallback ile sarıldığı için, ParentComponent her render edildiğinde yeniden oluşturulmaz. Bu, eğer handleClick bir alt bileşene prop olarak geçirilseydi ve o alt bileşen React.memo ile optimize edilmiş olsaydı, gereksiz yeniden render'ları engellerdi.

  • useMemo: Hesaplama maliyeti yüksek olan değerleri belleğe almak için kullanılır. Bağımlılıkları değişmediği sürece, hesaplama sonucunu yeniden hesaplamaz, önbelleğe alınmış değeri döndürür.
    
    import React, { useState, useMemo } from 'react';
    
    function HesaplamaComponent() {
      const [sayi1, setSayi1] = useState(0);
      const [sayi2, setSayi2] = useState(0);
    
      // Yüksek maliyetli bir hesaplama olduğunu varsayalım
      const toplam = useMemo(() => {
        console.log('Toplama işlemi yeniden yapıldı!');
        return sayi1 + sayi2;
      }, [sayi1, sayi2]); // Sadece sayi1 veya sayi2 değiştiğinde yeniden hesapla
    
      return (
        
    setSayi1(Number(e.target.value))} /> setSayi2(Number(e.target.value))} />

    Toplam: {toplam}

    ); } export default HesaplamaComponent;

    toplam değeri, sayi1 veya sayi2 değişmediği sürece yeniden hesaplanmaz. Bu, özellikle karmaşık filtreleme, sıralama veya büyük veri kümeleri üzerinde yapılan diğer yoğun hesaplamalar için performans açısından kritik öneme sahiptir.

Uzman İpucu: Bu optimizasyon araçlarını dikkatli kullanın. Her zaman her yerde memoization yapmak, çoğu zaman gereksiz yere karmaşıklık ve hafıza tüketimi yaratır. Yalnızca performans darboğazlarını tespit ettiğiniz noktalarda uygulayın. Gereksiz kullanım, uygulamanızın daha yavaş çalışmasına neden olabilir, çünkü memoization'ın da kendi içinde bir maliyeti vardır.

6. Error Boundaries: Uygulamanızı Kilitlenmelerden Koruyun

Büyük uygulamalarda, bir bileşende oluşan küçük bir hata bile tüm uygulamanın çökmesine neden olabilir. React'in Error Boundaries (Hata Sınırları) pattern'ı, bileşen ağacının belirli bir bölümündeki JavaScript hatalarını yakalamanıza, bu hataları günlüğe kaydetmenize ve kullanıcıya yedek bir UI göstermenize olanak tanır.

Error Boundaries Nedir? Bir React sınıf bileşenidir ve çocuk bileşen ağacında render sırasında, yaşam döngüsü yöntemlerinde veya kurucularda oluşan JavaScript hatalarını yakalar. Fonksiyonel bileşenler hata sınırları olamaz, ancak bir hata sınırı içine yerleştirilebilirler.


// ErrorBoundary.js
import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null, errorInfo: null };
  }

  static getDerivedStateFromError(error) {
    // Bir hata oluştuğunda, bir sonraki render için state'i güncelleyin
    return { hasError: true, error: error };
  }

  componentDidCatch(error, errorInfo) {
    // Hata bilgilerini bir hata raporlama servisine gönderebilirsiniz
    console.error("ErrorBoundary hatayı yakaladı: ", error, errorInfo);
    this.setState({ errorInfo: errorInfo });
    // Örneğin, Sentry gibi bir servise gönderebilirsiniz:
    // logErrorToMyService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      // Yedek UI'ı render edebilirsiniz
      return (
        

Oops! Bir şeyler ters gitti.

Uygulamamızda beklenmedik bir hata oluştu. Lütfen daha sonra tekrar deneyin.

{this.props.showDetails && this.state.error && (
Hata Detayları
{this.state.error.toString()}
{this.state.errorInfo.componentStack}

)}

);
}

return this.props.children;
}
}

export default ErrorBoundary;


// Hata Üretme Bileşeni (örneğin HataVerenBilesen.js)
import React from 'react';

function HataVerenBilesen({ shouldThrowError }) {
  if (shouldThrowError) {
    throw new Error('Bilerek bir hata oluşturdum!');
  }
  return 

Bu bileşen hatasız çalışıyor.

; } export default HataVerenBilesen;


// Uygulama.js içinde ErrorBoundary kullanımı
import React, { useState } from 'react';
import ErrorBoundary from './ErrorBoundary';
import HataVerenBilesen from './HataVerenBilesen';

function App() {
  const [hataUret, setHataUret] = useState(false);

  return (
    

Error Boundary Örneği

Aşağıdaki bileşen kendi hata sınırına sahip:

Bu kısım hata sınırının dışında ve etkilenmez.

); } export default App;

Bu örnekte, HataVerenBilesen kasıtlı olarak bir hata fırlattığında, ErrorBoundary bu hatayı yakalar ve kullanıcıya hoş bir yedek UI sunar, böylece uygulamanın geri kalanı çalışmaya devam eder. Bu, kullanıcı deneyimini önemli ölçüde artırır ve üretim ortamlarında uygulamanın daha sağlam olmasını sağlar.

Dikkat: Error Boundaries yalnızca render sırasında oluşan hataları, yaşam döngüsü yöntemlerindeki hataları ve kuruculardaki hataları yakalar. Olay yöneticilerindeki (event handlers) hataları, asenkron kodlardaki hataları (setTimeout veya requestAnimationFrame callback'leri) veya sunucu tarafında oluşan hataları yakalamaz. Bu tür hatalar için try-catch blokları kullanmalısınız.

Mobil Uyumlu React Tasarımı: Media Query Örnekleri

Günümüzde mobil cihazlardan erişilen web sitelerinin oranı masaüstü cihazları geride bırakmıştır. Bu nedenle, React uygulamalarınızın farklı ekran boyutlarında sorunsuz çalışması, yani responsiv (duyarlı) olması hayati önem taşır. React'in kendi başına responsive tasarım için özel bir pattern'ı olmasa da, CSS ve JavaScript kullanarak bu yeteneği bileşenlerinize entegre edebilirsiniz. CSS media query'leri, responsive tasarımın temelini oluşturur. İşte bir örnek:


// ResponsiveCard.js (CSS Modülleri ile Mobil Uyumlu Tasarım)
import React from 'react';
import styles from './ResponsiveCard.module.css'; // CSS Modüllerini import ediyoruz

function ResponsiveCard({ title, content }) {
  return (
    

{title}

{content}

); } export default ResponsiveCard;


/* ResponsiveCard.module.css */
.card {
  width: 90%;
  max-width: 400px;
  margin: 20px auto;
  padding: 20px;
  border: 1px solid #ccc;
  box-shadow: 0 2px 5px rgba(0,0,0,0.1);
  text-align: center;
  background-color: #f9f9f9;
}

.cardTitle {
  color: #333;
  font-size: 1.5em;
  margin-bottom: 10px;
}

.cardContent {
  color: #666;
  font-size: 1em;
  line-height: 1.5;
}

.cardButton {
  background-color: #007bff;
  color: white;
  padding: 10px 15px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  margin-top: 15px;
}

/* Mobil cihazlar için stil tanımlamaları (örneğin, 768px altı) */
@media (max-width: 768px) {
  .card {
    width: 95%; /* Daha dar ekranlarda daha fazla yer kapla */
    padding: 15px;
    margin: 10px auto;
  }

  .cardTitle {
    font-size: 1.2em; /* Mobil için daha küçük başlık */
  }

  .cardContent {
    font-size: 0.9em; /* Mobil için daha küçük içerik */
  }

  .cardButton {
    padding: 8px 12px;
    font-size: 0.9em;
  }
}

/* Daha küçük mobil cihazlar için stil tanımlamaları (örneğin, 480px altı) */
@media (max-width: 480px) {
  .card {
    border-radius: 0; /* Köşeleri yuvarlama */
    box-shadow: none; /* Gölgeyi kaldır */
  }
  .cardButton {
    width: 100%; /* Mobil cihazlarda tam genişlik */
  }
}
            

Bu örnekte, ResponsiveCard bileşenimiz CSS Modülleri kullanarak stilize edilmiştir. ResponsiveCard.module.css dosyasında, standart stillere ek olarak @media kuralları ile farklı ekran boyutlarına göre özelleştirilmiş stiller tanımlanmıştır. Bu sayede, kart bileşeni mobil cihazlarda daha okunaklı ve kullanışlı bir şekilde görüntülenir.

Uzman İpucu: Responsive tasarımlar için sadece CSS Media Query'leri kullanmakla kalmayıp, react-responsive gibi kütüphanelerle JavaScript üzerinden de ekran boyutlarına göre koşullu render yapabilirsiniz. Bu, özellikle farklı ekranlarda tamamen farklı bileşenler göstermeniz gerektiğinde işe yarar.

Performans Karşılaştırmaları: Ne Zaman Ne Kullanılmalı?

Farklı React pattern'larının kendine özgü kullanım alanları ve performans etkileri vardır. Bir pattern'ı seçerken, sadece kodun okunabilirliği veya yeniden kullanılabilirliği değil, aynı zamanda uygulamanın genel performansı üzerindeki etkisi de göz önünde bulundurulmalıdır. İşte bazı önemli pattern'lar ve performans düşünceleri:

Pattern Temel Avantaj Performans Etkisi Ne Zaman Tercih Edilmeli?
Bileşen Kompozisyonu Modülerlik, yeniden kullanılabilirlik Çok düşük. Temel mekanizmadır. Her zaman. React'in felsefesinin temelidir.
Container/Presenter Sorumlulukların ayrılması, test edilebilirliği artırma Düşük. Ekstra bir sarmalama katmanı getirir. Karmaşık iş mantığı olan büyük bileşenler için.
Context API Prop drilling'i önleme, global state yönetimi Orta. Context değiştiğinde tüm tüketiciler yeniden render olur. Gereksiz kullanım performans düşüşüne yol açabilir. Uygulama genelinde sık değişmeyen veya az sayıda tüketicisi olan veriler (tema, dil, kimlik doğrulama).
Custom Hooks Stateful mantığı yeniden kullanma, kod tekrarını azaltma Düşük. Fonksiyonel maliyeti minimaldir. Bileşenler arası benzer iş mantığı paylaşmanız gerektiğinde. Modern React geliştirme için en çok tercih edilen yöntem.
Render Props Dinamik UI render'ı, mantık paylaşımı Orta. Fonksiyon prop olarak iletildiği için her render'da yeni bir fonksiyon oluşur, bu da child bileşenlerin gereksiz render olmasına neden olabilir (React.memo ile kullanılmadığında). UI'nın dinamik olarak değiştiği ve mantık paylaşımının esnek olması gereken durumlarda. Hooks öncesi daha popülerdi.
Higher-Order Components (HOCs) Bileşenlere ekstra özellikler katma, mantık paylaşımı Orta. Ekstra bir bileşen sarmalama katmanı oluşturur. Prop çakışmaları ve debug zorlukları olabilir. Belirli bir özelliği (örn: yetkilendirme, veri yükleme) birden fazla bileşene eklemeniz gerektiğinde. Hooks öncesi yaygın olarak kullanılırdı. Genellikle Custom Hooks ile değiştirilebilirler.
React.memo, useCallback, useMemo Gereksiz yeniden render'ları engelleme, hesaplama maliyetini düşürme Yüksek performans kazancı (doğru kullanıldığında). Ancak her memoization'ın da bir maliyeti vardır. Performans darboğazlarını giderirken, maliyetli hesaplamalar veya sık sık yeniden render olan child bileşenleri optimize ederken. Profiler ile tespit edilen alanlarda kullanılmalı.
Error Boundaries Uygulama kararlılığı, hatalara karşı direnç Çok düşük. Sadece hata anında devreye girer. Uygulamanızın kritik bölümlerini hatalara karşı korumak için her zaman.

Performans optimizasyonları yaparken her zaman bir React profiler (örneğin React DevTools ile gelen) kullanmak önemlidir. "Premature optimization is the root of all evil" (Erken optimizasyon tüm kötülüklerin köküdür) ilkesini unutmayın. Önce uygulamanızı çalışır hale getirin, sonra performans sorunları yaşadığınızda optimize edin.

Sonuç: React Uzmanlığına Giden Yolculuğunuz

Bu kapsamlı makale boyunca, React ekosistemindeki en güçlü ve yaygın tasarım kalıplarını derinlemesine inceledik. Basit bileşen kompozisyonlarından başlayarak, veri ve iş mantığını ayırma stratejilerine (Container/Presenter), prop drilling sorununu çözmeye (Context API), stateful mantığı yeniden kullanmaya (Custom Hooks), esnek UI oluşturmaya (Render Props), ilişkili bileşen gruplarını yönetmeye (Compound Components), bileşenlere ek özellikler katmaya (HOCs) ve son olarak uygulamanızın performansını ve sağlamlığını artırmaya (React.memo, useCallback, useMemo, Error Boundaries) yönelik çeşitli yaklaşımları öğrendik.

Her bir pattern, belirli bir problemi çözmek için tasarlanmıştır ve hangi senaryoda hangi pattern'ı kullanacağınızı bilmek, sizi iyi bir React geliştiricisinden bir uzmana dönüştürecektir. Unutmayın, bu pattern'lar birer kılavuzdur, dogma değildir. Projenizin özel ihtiyaçlarına göre bunları uyarlamak veya bazen farklı yaklaşımlar denemek sizin elinizde. Önemli olan, arkasındaki prensipleri anlamak ve temiz, ölçeklenebilir ve bakımı kolay kod yazma felsefesini benimsemektir.

React sürekli gelişen bir teknolojidir ve yeni hook'lar veya özellikler, eski pattern'ların yerini alabilir veya onları geliştirebilir. Bu nedenle, öğrenmeye ve kendinizi güncel tutmaya devam etmek büyük önem taşır. Bu makalede edindiğiniz bilgilerle, React projelerinizde daha bilinçli kararlar alacak ve daha sağlam, performanslı uygulamalar geliştireceksiniz. Başarılar dileriz!

Sıkça Sorulan Sorular

  1. Custom Hooks ve HOCs arasındaki temel fark nedir ve hangisi ne zaman tercih edilmeli?

    Fark: Custom Hooks, fonksiyonel bileşenlerde stateful mantığı yeniden kullanmak için kullanılır ve doğrudan React'in hook yapısına dayanır. HOCs ise bir bileşeni saran ve ona yeni prop'lar veya davranışlar ekleyen bir fonksiyondur. HOC'lar genellikle sınıf bileşenleri döneminden kalma bir pattern'dır.

    Tercih: Modern React uygulamalarında, stateful mantığı ve yan etkileri paylaşmak için genellikle Custom Hooks tercih edilir. Daha az "wrapper hell" riski taşır, daha okunabilir ve fonksiyonel bileşenlerin doğasına daha uygundur. HOC'lar hala bazı eski kod tabanlarında veya belirli cross-cutting concerns (örneğin yetkilendirme) için kullanılabilir ancak yeni projelerde Custom Hooks daha esnek ve modern bir seçenektir.

  2. Context API, Redux gibi global state yönetim kütüphanelerinin yerini alabilir mi?

    Hayır, Context API Redux gibi karmaşık global state yönetim kütüphanelerinin tam olarak yerini almaz. Context API, daha küçük, daha basit ve uygulamanın belirli bir bölümünde paylaşılan veriler (tema, dil ayarları gibi) için harikadır. Redux ise daha büyük, karmaşık uygulamalarda tahmin edilebilir state yönetimi, zaman yolculuğu hata ayıklaması ve merkezi state yönetimi gibi daha gelişmiş özellikler sunar. Context API'nin aşırı kullanımı, performans sorunlarına yol açabilir çünkü bir Context değeri değiştiğinde, onu tüketen tüm bileşenler yeniden render edilir. Karmaşık state değişiklikleri ve türetilmiş state'ler için Redux veya Recoil/Jotai gibi kütüphaneler daha uygundur.

  3. React.memo, useCallback ve useMemo kullanmak her zaman performansı artırır mı?

    Hayır, her zaman artırmaz. Bu optimizasyon araçları, gereksiz yeniden render'ları ve pahalı hesaplamaları önlemek için kullanılır, ancak kendilerinin de bir maliyeti vardır (bellek kullanımı ve kontrol mekanizması için CPU zamanı). Küçük ve basit bileşenler veya az sıklıkla güncellenen değerler için bu hook'ları kullanmak, çoğu zaman gereksiz karmaşıklık yaratır ve hatta performans düşüşüne neden olabilir. Yalnızca React DevTools Profiler gibi araçlarla performans darboğazları tespit edildiğinde ve bu optimizasyonların gerçekten fayda sağlayacağı anlaşıldığında kullanılmaları önerilir.

  4. Error Boundaries neleri yakalar ve neleri yakalamaz?

    Yakaladığı hatalar: Render sırasında (render metodunda), yaşam döngüsü yöntemlerinde (componentDidMount, componentDidUpdate vb.) ve sınıf bileşenlerinin kurucularında (constructor) oluşan JavaScript hataları.

    Yakalayamadığı hatalar: Olay yöneticileri içindeki hatalar (onClick, onChange gibi), asenkron kodlardaki hatalar (setTimeout, requestAnimationFrame callback'leri, Promise'ler), sunucu tarafında oluşan hatalar (SSR/SSG), kendi içindeki hatalar (bir Error Boundary kendisi render ederken bir hata fırlatırsa). Bu durumlar için manuel try-catch blokları veya diğer hata yönetimi stratejileri kullanılmalıdır.

Daha fazla detay ve güncel geliştirme ipuçları için Fatih Soysal'ın blogunu ziyaret edebilirsiniz.

Yazar: Fatih Soysal

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

Gönder

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.
Exit mobile version