Takip et

React Uygulamanız Neden Yavaş Hissediyor? 3 Temel Sebep ve Çözümleri

<div class="code-container"> <pre><code> import React from 'react'; // Normal bir bileşen const ListItem = ({ item, onClick }) => { console.

React Uygulamanız Neden Yavaş Hissediyor? 3 Temel Sebep ve Çözümleri

Günümüz web uygulamalarında kullanıcı deneyimi (UX) her şeyden önemli hale geldi. Hızlı yüklenen, akıcı çalışan bir uygulama, kullanıcıların sitenizde daha uzun süre kalmasını sağlarken, yavaş ve takılan bir uygulama onları anında kaçırabilir. React, modern web geliştirmede popüler bir tercih olsa da, yanlış kullanıldığında veya optimize edilmediğinde uygulamanızın performansını olumsuz etkileyebilir. Bu makalede, React uygulamalarınızın neden yavaş hissedebileceğine dair üç temel nedeni ve bu sorunları nasıl çözebileceğinizi adım adım inceleyeceğiz. Eğer React uygulamanızın hızı sizi endişelendiriyorsa, doğru yerdesiniz.

React, bileşen tabanlı yapısı ve sanal DOM (Virtual DOM) yaklaşımı sayesinde hızlı ve verimli kullanıcı arayüzleri oluşturmak için tasarlanmıştır. Ancak bu potansiyeli tam olarak kullanabilmek için React’in çalışma prensiplerini iyi anlamak ve olası performans darboğazlarını bilmek gerekir. Uygulamanızın yavaşlamasına neden olan temel faktörler genellikle gereksiz yeniden oluşturmalar (re-renders), büyük ve optimize edilmemiş paket boyutları (bundle sizes) ve verimsiz veri yönetimi ile ağ istekleri etrafında döner. Bu sorunları doğru stratejilerle ele alarak, kullanıcılarınıza daha hızlı ve akıcı bir deneyim sunabilirsiniz. İşte bu üç temel sorunu ve çözüm yollarını detaylıca ele alalım.

1. Gereksiz Yeniden Oluşturmalar (Re-renders) Nasıl Engellenir?

React uygulamalarının yavaşlamasının en yaygın nedenlerinden biri, bileşenlerin gereksiz yere yeniden oluşturulmasıdır (re-render). React, bir bileşenin durumu (state) veya özelliklerinde (props) bir değişiklik olduğunda, o bileşeni ve alt bileşenlerini yeniden oluşturur. Sanal DOM sayesinde bu işlem oldukça hızlı gerçekleşse de, çok sayıda bileşenin sürekli olarak ve gereksiz yere yeniden oluşturulması performansı ciddi şekilde etkileyebilir. Özellikle büyük ve karmaşık uygulamalarda, bir üst bileşendeki önemsiz bir durum değişikliği, tüm alt bileşen ağacının baştan aşağı yeniden render edilmesine yol açabilir. Bu durum, özellikle kullanıcı etkileşimlerinin yoğun olduğu veya verilerin sık güncellendiği arayüzlerde fark edilir bir gecikmeye neden olabilir. Örneğin, bir liste uygulamasında, tek bir öğenin durumunun değişmesi tüm listenin yeniden oluşturulmasına yol açarsa, bu durum özellikle uzun listelerde kullanıcı deneyimini olumsuz etkiler.

Peki, bu gereksiz yeniden oluşturmaları nasıl tespit edip engelleyebiliriz?

* React.memo ile Bileşenleri Hafızaya Almak:
React.memo, fonksiyonel bileşenler için bir üst düzey bileşen (Higher-Order Component – HOC) görevi görür. Bu HOC, bileşenin özelliklerinin (props) değişip değişmediğini kontrol eder. Eğer özellikler değişmediyse, React bileşeni yeniden oluşturmak yerine hafızadaki (memoized) son çıktıyı kullanır. Bu sayede, aynı özelliklerle çağrılan bileşenlerin tekrar tekrar render edilmesi engellenir. Özellikle sık sık render edilen ve performans açısından kritik olan bileşenlerde React.memo kullanımı büyük fayda sağlar. Varsayılan olarak, React.memo özelliklerin sığ karşılaştırmasını (shallow comparison) yapar. Eğer daha derin bir karşılaştırma yapmanız gerekiyorsa, ikinci bir argüman olarak özel bir karşılaştırma fonksiyonu ((prevProps, nextProps) => boolean) sağlayabilirsiniz.

<div class="code-container">
      <pre><code>
    import React from 'react';

    // Normal bir bileşen
    const ListItem = ({ item, onClick }) => {
      console.log('ListItem render edildi:', item.id);
      return (
        <li onClick={() => onClick(item.id)}>
          {item.name}
        </li>
      );
    };

    // React.memo ile optimize edilmiş bileşen
    const MemoizedListItem = React.memo(ListItem);

    function MyList({ items, onItemClick }) {
      return (
        <ul>
          {items.map(item => (
            <MemoizedListItem key={item.id} item={item} onClick={onItemClick} />
          ))}
        </ul>
      );
    }
      </code></pre>
    </div>

Yukarıdaki örnekte, MemoizedListItem sadece item veya onClick prop’u değiştiğinde yeniden render edilecektir. Bu, özellikle bir listedeki diğer öğeler değiştiğinde, sadece ilgili öğenin render edilmesini sağlayarak performansı artırır.

* useMemo ve useCallback Hook’ları ile Değerleri ve Fonksiyonları Hafızaya Almak:
useMemo ve useCallback hook’ları, sırasıyla hesaplanmış değerleri ve fonksiyonları hafızaya almak için kullanılır.
* useMemo: Bir fonksiyonun sonucunu, bağımlılıkları değişmediği sürece yeniden hesaplamamak için kullanılır. Özellikle pahalı hesaplamalar içeren veya render sırasında yeni bir referans oluşturan nesneler, diziler gibi değerleri hafızaya almak için idealdir. Örneğin, büyük bir veri kümesini filtreleme veya sıralama gibi işlemlerin sonucunu useMemo ile önbelleğe alabilirsiniz.

<div class="code-container">
          <pre><code>
        import React, { useState, useMemo } from 'react';

        function ProductList({ products, filterText }) {
          const filteredProducts = useMemo(() => {
            console.log('Ürünler filtreleniyor...');
            return products.filter(product =>
              product.name.toLowerCase().includes(filterText.toLowerCase())
            );
          }, [products, filterText]); // products veya filterText değiştiğinde yeniden hesapla

          return (
            <div>
              {filteredProducts.map(product => (
                <div key={product.id}>{product.name}</div>
              ))}
            </div>
          );
        }
          </code></pre>
        </div>
        Bu örnekte, filteredProducts sadece products veya filterText değiştiğinde yeniden hesaplanacaktır. Aksi takdirde, önbellekteki değer kullanılır.

    *   useCallback: Bir fonksiyon referansını, bağımlılıkları değişmediği sürece hafızaya almak için kullanılır. Özellikle React.memo ile optimize edilmiş alt bileşenlere prop olarak geçirilen fonksiyonlar için çok önemlidir. JavaScript'te, her render işleminde yeni bir fonksiyon referansı oluşturulur. Eğer bu yeni fonksiyon referansı React.memo'lu bir alt bileşene prop olarak geçirilirse, alt bileşen prop'un değiştiğini düşünerek gereksiz yere yeniden render edilir. useCallback bu durumu engelleyerek fonksiyon referansının sabit kalmasını sağlar.

html


        import React, { useState, useCallback } from 'react';

        const Button = React.memo(({ onClick, text }) => {
          console.log('Button render edildi:', text);
          return <button onClick={onClick}>{text}</button>;
        });

        function ParentComponent() {
          const [count, setCount] = useState(0);

          // Her renderda yeni bir fonksiyon oluşturulmasını engeller
          const handleClick = useCallback(() => {
            setCount(prevCount => prevCount + 1);
          }, []); // Bağımlılık dizisi boş olduğu için sadece bir kere oluşturulur

          return (
            <div>
              <p>Sayaç: {count}</p>
              <Button onClick={handleClick} text="Arttır" />
              <Button onClick={() => console.log('Başka bir buton')} text="Diğer Buton" />
            </div>
          );
        }
          

Burada handleClick fonksiyonu useCallback ile hafızaya alınmıştır. ParentComponent yeniden render edildiğinde bile, handleClick‘in referansı değişmeyeceği için Button bileşeni gereksiz yere render edilmeyecektir (eğer text prop’u da değişmezse).

* Context API Optimizasyonu:
Context API, global durum yönetimi için kullanışlıdır ancak dikkatli kullanılmadığında gereksiz yeniden oluşturmalara yol açabilir. Bir Context sağlayıcısının (Provider) değeri değiştiğinde, o Context’i tüketen tüm bileşenler yeniden render edilir. Bu sorunu aşmak için:
* Context’i Bölmek: Farklı durum parçaları için ayrı Context’ler oluşturarak, sadece ilgili durum değiştiğinde ilgili bileşenlerin render edilmesini sağlayabilirsiniz.
* Değeri Hafızaya Almak: Context sağlayıcısına geçirilen değeri useMemo ile hafızaya alarak, referansının gereksiz yere değişmesini engelleyebilirsiniz.

<div class="code-container">
      <pre><code>
    import React, { createContext, useState, useMemo, useContext } from 'react';

    // Kullanıcı bilgileri için ayrı bir Context
    const UserContext = createContext();
    // Tema bilgileri için ayrı bir Context
    const ThemeContext = createContext();

    function App() {
      const [user, setUser] = useState({ name: 'Ali', email: 'ali@example.com' });
      const [theme, setTheme] = useState('light');

      // UserContext değeri sadece 'user' state'i değiştiğinde güncellenir
      const userValue = useMemo(() => ({ user, setUser }), [user]);
      // ThemeContext değeri sadece 'theme' state'i değiştiğinde güncellenir
      const themeValue = useMemo(() => ({ theme, setTheme }), [theme]);

      return (
        <UserContext.Provider value={userValue}>
          <ThemeContext.Provider value={themeValue}>
            <UserProfile />
            <ThemeSwitcher />
          </ThemeContext.Provider>
        </UserContext.Provider>
      );
    }

    const UserProfile = React.memo(() => {
      const { user } = useContext(UserContext);
      console.log('UserProfile render edildi');
      return (
        <div>
          <h3>Kullanıcı Profili</h3>
          <p>Ad: {user.name}</p>
          <p>Email: {user.email}</p>
        </div>
      );
    });

    const ThemeSwitcher = React.memo(() => {
      const { theme, setTheme } = useContext(ThemeContext);
      console.log('ThemeSwitcher render edildi');
      return (
        <div>
          <h3>Tema Seçimi</h3>
          <p>Mevcut Tema: {theme}</p>
          <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
            Temayı Değiştir
          </button>
        </div>
      );
    });
      </code></pre>
    </div>
    Bu yapıda, UserProfile bileşeni sadece kullanıcı bilgileri değiştiğinde, ThemeSwitcher ise sadece tema değiştiğinde yeniden render edilecektir. Bu, Context API kullanımından kaynaklanan gereksiz render'ları minimize eder.

Bu teknikleri doğru bir şekilde uygulayarak, React uygulamanızdaki gereksiz yeniden oluşturmaları önemli ölçüde azaltabilir ve genel performansı artırabilirsiniz. Unutmayın, her bileşene React.memo eklemek yerine, öncelikle performans darboğazlarını tespit etmek için React DevTools gibi araçları kullanmak ve sadece kritik bileşenleri optimize etmek daha verimli bir yaklaşımdır.

<h3>2. Büyük ve Optimize Edilmemiş Bundle Boyutları Nasıl Küçültülür?</h3>

React uygulamalarının yavaş hissetmesinin bir diğer önemli nedeni, uygulamanın toplam paket (bundle) boyutunun gereğinden fazla büyük olmasıdır. Kullanıcı tarayıcıya uygulamanızı yüklemeye çalıştığında, bu büyük JavaScript paketlerinin indirilmesi, ayrıştırılması (parsing) ve yürütülmesi zaman alır. Özellikle mobil cihazlarda veya yavaş internet bağlantılarında bu durum, uygulamanın ilk yükleme süresini (initial load time) önemli ölçüde uzatarak kullanıcı deneyimini olumsuz etkiler. Bir e-ticaret sitesinin açılış sayfasının 5-10 saniye beklemesi, potansiyel müşterilerin çoğunu kaybetmek anlamına gelir. Büyük paketler, sadece indirme süresini değil, aynı zamanda tarayıcının JavaScript kodunu işleme süresini de artırır. Bu da uygulamanın etkileşimli hale gelmesi için geçen süreyi (Time to Interactive) uzatır.

Peki, bu paket boyutlarını nasıl optimize edebilir ve uygulamanızın daha hızlı yüklenmesini sağlayabiliriz?

*   Kod Bölme (Code Splitting) ile Dinamik Yükleme:
    Kod bölme, uygulamanızın tamamını tek bir büyük JavaScript dosyası yerine, daha küçük parçalara (chunk'lara) bölme tekniğidir. Bu parçalar, yalnızca ihtiyaç duyulduğunda dinamik olarak yüklenir. Örneğin, bir kullanıcının uygulamanızın bir bölümüne (örneğin, yönetici paneli) asla gitmeyeceği durumlarda, o bölümün kodunu uygulamanın ilk yüklemesiyle birlikte indirmeye gerek yoktur. React, React.lazy ve Suspense bileşenleri ile bu işlemi kolaylaştırır.

html


    import React, { Suspense } from 'react';

    // AdminDashboard bileşenini sadece ihtiyaç duyulduğunda yükle
    const AdminDashboard = React.lazy(() => import('./AdminDashboard'));
    const UserProfile = React.lazy(() => import('./UserProfile'));

    function App() {
      const [showAdmin, setShowAdmin] = React.useState(false);
      const [showProfile, setShowProfile] = React.useState(false);

      return (
        <div>
          <h1>Uygulama Ana Sayfası</h1>
          <button onClick={() => setShowAdmin(true)}>Admin Paneline Git</button>
          <button onClick={() => setShowProfile(true)}>Profilime Git</button>

          <Suspense fallback={<div>Yükleniyor...</div>}>
            {showAdmin && <AdminDashboard />}
            {showProfile && <UserProfile />}
          </Suspense>
        </div>
      );
    }
      

Yukarıdaki örnekte, AdminDashboard ve UserProfile bileşenleri yalnızca showAdmin veya showProfile durumu true olduğunda yüklenecektir. Suspense ise yükleme süresince bir yedek (fallback) içerik gösterir. Bu, özellikle farklı rotalar (routes) için veya modallar (modals) gibi koşullu olarak render edilen bileşenler için oldukça etkilidir.

* Tree Shaking (Ağaç Sallama):
Tree shaking, modern JavaScript derleyicileri (bundlers) tarafından uygulanan bir optimizasyon tekniğidir. Bu teknik, uygulamanızda gerçekten kullanılan kod parçalarını belirler ve kullanılmayanları (dead code) nihai paketten çıkarır. Örneğin, bir kütüphaneden sadece birkaç fonksiyon kullanıyorsanız, tree shaking o kütüphanenin tamamını değil, sadece kullandığınız fonksiyonları paketinize dahil etmenizi sağlar.
* Nasıl Etkinleştirilir?: Çoğu modern React projesi (Create React App, Next.js, Vite gibi araçlarla oluşturulanlar) varsayılan olarak tree shaking’i destekler. Ancak, kütüphanelerinizi import ederken dikkatli olmanız gerekir. named imports (import { func1, func2 } from 'library') kullanmak, default imports’a (import library from 'library') göre tree shaking için daha etkilidir.
* Kütüphane Seçimi: Kütüphane seçimi yaparken, modüler yapıda olan ve tree shaking’i destekleyen kütüphaneleri tercih etmek, paket boyutunu önemli ölçüde azaltabilir.

* Kütüphane Optimizasyonu ve Alternatifleri:
Uygulamanıza eklediğiniz her üçüncü taraf kütüphane, paket boyutunu artırır.
* Gereksiz Kütüphaneleri Kaldırın: Gerçekten ihtiyacınız olmayan kütüphaneleri projenizden çıkarın. Küçük bir fonksiyon için tüm bir kütüphaneyi dahil etmek yerine, kendi basit çözümünüzü yazmayı veya daha hafif bir alternatif bulmayı düşünün.
* Hafif Alternatifler Kullanın: Tarih işlemleri için moment.js yerine date-fns veya dayjs gibi daha hafif kütüphaneleri tercih edebilirsiniz. Benzer şekilde, büyük bir UI kütüphanesinin tamamını kullanmak yerine, sadece ihtiyacınız olan bileşenleri import edin veya daha modüler bir kütüphaneye yönelin. Örneğin, lodash yerine lodash-es kullanmak, tree shaking’in daha verimli çalışmasını sağlar.
* Sadece İhtiyaç Duyulanı İçe Aktarın: Eğer bir kütüphane modüler bir yapıya sahipse, sadece kullanacağınız kısımları içe aktarın. Örneğin, bir ikon kütüphanesinden tüm ikon setini değil, sadece belirli ikonları içe aktarın.

<div class="code-container">
      <pre><code>
    // Kötü örnek: Tüm lodash kütüphanesini içe aktarmak
    import _ from 'lodash';
    const result = _.debounce(myFunction, 500);

    // İyi örnek: Sadece ihtiyaç duyulan fonksiyonu içe aktarmak (tree shaking dostu)
    import debounce from 'lodash/debounce';
    const result = debounce(myFunction, 500);
      </code></pre>
    </div>

*   Görsel Optimizasyonu:
    JavaScript paket boyutu önemli olsa da, görsellerin boyutu da uygulamanın genel yükleme süresini ciddi şekilde etkiler.
    *   Doğru Formatlar: Web için uygun formatlar (JPEG, PNG, WebP) kullanın. WebP, genellikle daha küçük dosya boyutları sunar.
    *   Sıkıştırma: Görselleri kaliteden ödün vermeden sıkıştırın. Çevrimiçi araçlar veya derleme aşamasında otomatik sıkıştırma eklentileri kullanabilirsiniz.
    *   Duyarlı Görseller (Responsive Images): Farklı ekran boyutları için farklı çözünürlüklerde görseller sunarak, küçük ekranlı cihazların gereksiz yere büyük görseller indirmesini engelleyin. <img srcset="..."> veya <picture> etiketlerini kullanın.
    *   Tembel Yükleme (Lazy Loading): Görselleri yalnızca kullanıcı ekranına yaklaştığında yükleyin. loading="lazy" niteliğini veya JavaScript kütüphanelerini kullanabilirsiniz.

Bu optimizasyon tekniklerini uygulayarak, React uygulamanızın paket boyutunu küçültebilir, ilk yükleme süresini hızlandırabilir ve genel kullanıcı deneyimini önemli ölçüde iyileştirebilirsiniz. Unutmayın, bu bir defalık bir işlem değil, uygulamanız geliştikçe sürekli olarak izlemeniz ve optimize etmeniz gereken bir süreçtir.

<h3>3. Veri Yönetimi ve Ağ İstekleri Nasıl İyileştirilir?</h3>

React uygulamalarının yavaşlamasının üçüncü ve belki de en karmaşık nedeni, verimsiz veri yönetimi ve ağ istekleridir. Bir uygulama ne kadar iyi optimize edilirse edilsin, sunucudan veri çekme (data fetching) ve bu veriyi uygulama içinde yönetme süreçleri kötü tasarlandığında, kullanıcılar yine de gecikmeler ve takılmalar yaşayabilir. Özellikle veri yoğun uygulamalarda, sık ve tekrar eden ağ istekleri, büyük veri setlerinin işlenmesi veya durum yönetiminin karmaşıklığı performansı doğrudan etkiler. Bir finans uygulamasında anlık veri güncellemelerinin gecikmesi veya bir sosyal medya akışının yavaş yüklenmesi, kullanıcı memnuniyetini derinden sarsar.

Bu sorunları aşmak ve uygulamanızın veri akışını hızlandırmak için aşağıdaki stratejileri uygulayabiliriz:

*   Veri Önbellekleme (Data Caching) ve Otomatik Yeniden Doğrulama:
    Her kullanıcı etkileşiminde aynı veriyi sunucudan tekrar tekrar çekmek yerine, veriyi önbelleğe almak (cache) performansı büyük ölçüde artırır. react-query (şimdiki adıyla TanStack Query) veya SWR gibi kütüphaneler, veri çekme, önbellekleme, senkronizasyon ve arka plan güncellemeleri gibi işlemleri yönetmek için güçlü çözümler sunar. Bu kütüphaneler:
    *   Otomatik Önbellekleme: Çekilen veriyi otomatik olarak önbelleğe alır ve aynı istek tekrarlandığında sunucuya gitmek yerine önbellekten döner.
    *   Arka Plan Yeniden Doğrulama (Background Revalidation): Kullanıcıya önbellekteki veriyi gösterirken, arka planda sunucudan en güncel veriyi çekerek önbelleği günceller. Bu sayede kullanıcıya anında içerik sunulur ve verinin güncel olduğu hissi verilir.
    *   Yeniden Deneme (Retries) ve Hata Yönetimi: Ağ hatalarında otomatik yeniden deneme yapar ve hata durumlarını daha kolay yönetmenizi sağlar.
    *   Eski Veri Gösterimi (Stale-While-Revalidate): Önbellekteki eski veriyi hemen gösterip, arka planda yeni veriyi çekerek kullanıcı deneyimini kesintisiz hale getirir.

html


    import React from 'react';
    import { useQuery, QueryClient, QueryClientProvider } from '@tanstack/react-query';

    const queryClient = new QueryClient();

    async function fetchProducts() {
      const response = await fetch('/api/products');
      if (!response.ok) {
        throw new Error('Ürünler yüklenemedi');
      }
      return response.json();
    }

    function ProductsList() {
      const { data, isLoading, isError, error } = useQuery({
        queryKey: ['products'],
        queryFn: fetchProducts,
        staleTime: 5 * 60 * 1000, // 5 dakika boyunca "stale" sayılmaz
      });

      if (isLoading) return <div>Ürünler yükleniyor...</div>;
      if (isError) return <div>Hata: {error.message}</div>;

      return (
        <div>
          <h2>Ürün Listesi</h2>
          <ul>
            {data.map(product => (
              <li key={product.id}>{product.name} - {product.price} TL</li>
            ))}
          </ul>
        </div>
      );
    }

    function AppWithQuery() {
      return (
        <QueryClientProvider client={queryClient}>
          <ProductsList />
        </QueryClientProvider>
      );
    }
      

Bu örnekte, useQuery hook’u products verisini çekiyor ve otomatik olarak önbelleğe alıyor. Böylece aynı veri tekrar istendiğinde hızlıca geri dönüyor.

* Debouncing ve Throttling:
Kullanıcı arayüzünde belirli olaylar (örneğin, arama kutusuna yazma, pencere boyutunu değiştirme, kaydırma) çok sık tetiklenebilir. Bu olaylara bağlı olarak yapılan pahalı işlemler (örneğin, sunucuya API isteği gönderme, karmaşık hesaplamalar) uygulamanın yavaşlamasına neden olabilir.
* Debouncing (Geciktirme): Bir olayın belirli bir süre içinde tekrar tetiklenmediğinden emin olana kadar bir fonksiyonun çağrılmasını geciktirir. Örneğin, bir arama kutusuna yazı yazarken, her tuş vuruşunda API isteği göndermek yerine, kullanıcı yazmayı bıraktıktan 300ms sonra isteği göndermek için debounce kullanabilirsiniz.

<div class="code-container">
          <pre><code>
        import React, { useState, useEffect, useCallback } from 'react';
        import debounce from 'lodash/debounce'; // veya kendi debounce fonksiyonunuz

        function SearchBar() {
          const [searchTerm, setSearchTerm] = useState('');
          const [results, setResults] = useState([]);

          // API isteğini debounce ile sarmalama
          const debouncedSearch = useCallback(
            debounce(async (term) => {
              if (term.length > 2) {
                console.log('API isteği gönderiliyor:', term);
                // Gerçek bir API isteği burada yapılır
                const response = await fetch(/api/search?q=${term});
                const data = await response.json();
                setResults(data);
              } else {
                setResults([]);
              }
            }, 500), // 500ms gecikme
            []
          );

          useEffect(() => {
            debouncedSearch(searchTerm);
            return () => {
              debouncedSearch.cancel(); // Bileşen ayrıldığında gecikmiş çağrıları iptal et
            };
          }, [searchTerm, debouncedSearch]);

          const handleChange = (e) => {
            setSearchTerm(e.target.value);
          };

          return (
            <div>
              <input
                type="text"
                placeholder="Ara..."
                value={searchTerm}
                onChange={handleChange}
              />
              <ul>
                {results.map(item => (
                  <li key={item.id}>{item.name}</li>
                ))}
              </ul>
            </div>
          );
        }
          </code></pre>
        </div>
    *   Throttling (Kısıtlama): Bir fonksiyonun belirli bir zaman aralığında en fazla bir kez çağrılmasını sağlar. Örneğin, bir kaydırma olayına bağlı olarak bir animasyonu güncellemek veya bir API isteği göndermek için throttle kullanabilirsiniz.

*   Sayfalama (Pagination) ve Sonsuz Kaydırma (Infinite Scroll):
    Büyük veri setlerini (örneğin, binlerce ürün veya gönderi) tek seferde yüklemek ve render etmek hem ağ kaynaklarını tüketir hem de tarayıcının performansını düşürür.
    *   Sayfalama: Verileri belirli sayfalara bölerek, her seferinde sadece o sayfanın verisini yüklemeyi ve göstermeyi içerir. Bu, kullanıcının sadece ihtiyacı olan veriye odaklanmasını sağlar ve uygulamanın daha hızlı tepki vermesine yardımcı olur.
    *   Sonsuz Kaydırma: Kullanıcı sayfanın sonuna yaklaştıkça yeni verileri otomatik olarak yükler. Bu, özellikle sosyal medya akışları veya ürün listeleri için popüler bir yöntemdir. Ancak, sonsuz kaydırma uygularken, çok uzun listelerin DOM üzerinde birikmesini engellemek için liste sanallaştırma (list virtualization) tekniklerini kullanmak önemlidir.

*   İyimser Kullanıcı Arayüzü (Optimistic UI):
    Kullanıcı bir eylem gerçekleştirdiğinde (örneğin, bir gönderiyi beğenmek, bir öğeyi silmek), sunucudan onay beklemek yerine, kullanıcı arayüzünü (UI) hemen güncelleyerek işlemin başarılı olduğunu varsayın. Arka planda sunucu isteğini gönderin. Eğer sunucu isteği başarılı olursa, UI olduğu gibi kalır; başarısız olursa, kullanıcıya bir hata mesajı göstererek UI'yi eski haline döndürün. Bu teknik, kullanıcının uygulamanın anında tepki verdiğini hissetmesini sağlayarak algılanan performansı (perceived performance) önemli ölçüde artırır. Örneğin, bir "beğen" butonuna tıklandığında, hemen beğeni sayısını artırıp butonu aktif hale getirin, arka planda API isteğini gönderin.

Bu veri yönetimi ve ağ isteği optimizasyonları, React uygulamanızın sadece hızlı yüklenmesini değil, aynı zamanda etkileşimli ve akıcı bir deneyim sunmasını da sağlar. Doğru araçları ve yaklaşımları seçmek, uygulamanızın genel performansını ve kullanıcı memnuniyetini doğrudan etkileyecektir.

<h3>React Uygulamanızın Performansını İzleme ve İleri Düzey Optimizasyonlar</h3>

Uygulamanızın performansını iyileştirmek, sürekli bir süreçtir. Optimizasyonları uyguladıktan sonra, bunların gerçekten işe yarayıp yaramadığını anlamak için performansı izlemek kritik öneme sahiptir.

*   React DevTools ile Profilleme:
    React Developer Tools, tarayıcınız için bir eklenti olup, React bileşenlerinizin nasıl render edildiğini, hangi bileşenlerin ne kadar zaman harcadığını ve neden yeniden render edildiğini görmenizi sağlayan güçlü bir "Profiler" (profleyici) içerir. Bu araç sayesinde performans darboğazlarını görsel olarak tespit edebilir ve hangi bileşenlerin optimize edilmesi gerektiğini belirleyebilirsiniz. Özellikle "Why did this render?" (Bu neden render edildi?) özelliği, gereksiz yeniden oluşturmaların nedenlerini anlamanıza yardımcı olur.

html
<!– React DevTools’u tarayıcınıza yükleyin (Chrome, Firefox için mevcut) –>
<!– Uygulamanızı geliştirme modunda çalıştırın –>
<!– Tarayıcınızın geliştirici araçlarını açın ve “Profiler” sekmesine gidin –>
<!– Kayıt düğmesine basarak uygulamanızla etkileşime geçin ve performansı analiz edin –>

*   Liste Sanallaştırma (List Virtualization):
    Çok uzun listeler (örneğin, binlerce öğe içeren bir tablo), tüm öğeleri DOM'a render ettiğinizde performansı ciddi şekilde etkiler. Liste sanallaştırma, yalnızca kullanıcının o anda görüntüleyebileceği öğeleri (viewport içindeki öğeler) render ederek bu sorunu çözer. Kullanıcı kaydırdıkça, görünür hale gelen yeni öğeler render edilir ve görünürlükten çıkanlar DOM'dan kaldırılır. Bu sayede, DOM boyutu küçük tutulur ve render performansı artırılır. react-virtualized ve react-window gibi kütüphaneler bu işlevi sağlar.

html


    import React from 'react';
    import { FixedSizeList } from 'react-window';

    const Row = ({ index, style }) => (
      <div style={style}>
        Satır {index}
      </div>
    );

    function VirtualizedList() {
      const itemCount = 10000; // 10.000 öğeli bir liste
      const itemSize = 50; // Her öğenin yüksekliği

      return (
        <FixedSizeList
          height={400} // Liste konteynerinin yüksekliği
          width={300}  // Liste konteynerinin genişliği
          itemCount={itemCount}
          itemSize={itemSize}
        >
          {Row}
        </FixedSizeList>
      );
    }
      

Bu örnekte, 10.000 öğelik bir liste bile, react-window sayesinde sadece görünürdeki öğeler render edildiği için akıcı bir şekilde çalışacaktır.

* Web Workers ile Ağır Hesaplamaları Taşımak:
JavaScript, tek iş parçacıklı (single-threaded) bir dildir. Bu, ana iş parçacığında (main thread) yapılan uzun süreli veya yoğun hesaplamaların kullanıcı arayüzünü bloke etmesine (donmasına) neden olabileceği anlamına gelir. Web Workers, bu tür ağır hesaplamaları ayrı bir arka plan iş parçacığına taşımanıza olanak tanır. Böylece, kullanıcı arayüzü yanıt vermeye devam ederken, karmaşık veri işleme, görüntü manipülasyonu veya şifreleme gibi görevler arka planda yürütülebilir.

“html
<div class="code-container">
<pre><code>
// worker.js dosyası
// self.onmessage = function(e) {
// const result = e.data * 2; // Ağır bir hesaplama örneği
// self.postMessage(result);
// };

// React bileşeninde Web Worker kullanımı
import React, { useState, useEffect } from 'react';

function HeavyComputationComponent() {
const [result, setResult] = useState(0);

const doHeavyCalculation = () => {
if (window.Worker) {
const myWorker = new Worker(new URL('./worker.js', import.meta.url));
myWorker.postMessage(1000000000); // İşçiye veri gönder
myWorker.onmessage = function(e) {
setResult(e.data);
myWorker.terminate(); // İşçi görevini bitirince sonlandır
};
} else {
console.log('Web Workers desteklenmiyor.');
// Alternatif olarak ana iş parçacığında hesaplama yap
}
};

return (
<div>
<button onClick={doHeavyCalculation}>Ağır Hesaplama Yap</button>
<p>Sonuç: {result}</p>
</div>
);
}
</code></pre>
</div>
Bu teknik, özellikle büyük veri analizi veya karmaşık grafik işlemleri gibi CPU yoğun görevler için çok değerlidir.

Bu ileri düzey teknikler ve sürekli performans izleme, React uygulamanızın sadece hızlı olmasını değil, aynı zamanda büyük ölçekli ve karmaşık senaryolarda bile akıcı ve duyarlı kalmasını sağlar. Performans optimizasyonu, uygulamanızın yaşam döngüsü boyunca devam eden bir yolculuktur.

<h3>Sonuç</h3>

React uygulamalarının yavaş hissetmesinin ardında genellikle üç temel neden yatar: gereksiz yeniden oluşturmalar, büyük ve optimize edilmemiş paket boyutları ve verimsiz veri yönetimi ile ağ istekleri. Bu makalede ele aldığımız teknikler –React.memo,useMemo,useCallbackile gereksiz render'ları engellemek; kod bölme, tree shaking ve kütüphane optimizasyonu ile paket boyutlarını küçültmek;react-query` gibi araçlarla veri önbellekleme, debouncing/throttling ve sayfalama ile veri akışını hızlandırmak – uygulamanızın performansını önemli ölçüde artırmanıza yardımcı olacaktır.

Performans optimizasyonu, sadece daha hızlı bir uygulama sunmakla kalmaz, aynı zamanda kullanıcı memnuniyetini artırır, dönüşüm oranlarını yükseltir ve arama motoru sıralamalarında daha iyi yer almanızı sağlar. Unutmayın ki bu bir defalık bir görev değil, uygulamanızın geliştirme süreci boyunca sürekli olarak dikkat etmeniz gereken bir alandır. React DevTools gibi araçlarla düzenli olarak profil oluşturarak ve uygulamanızın performansını izleyerek, her zaman en iyi kullanıcı deneyimini sunabilirsiniz.

Sıkça Sorulan Sorular

  1. React.memo ve useMemo arasındaki fark nedir?

    React.memo, bir fonksiyonel bileşenin kendisini hafızaya almak için kullanılır. Yani, bileşene gelen özellikler (props) değişmediği sürece bileşenin yeniden render edilmesini engeller. useMemo ise, bir fonksiyonun döndürdüğü değeri hafızaya almak için kullanılır. Bağımlılıkları değişmediği sürece pahalı hesaplamaların veya yeni nesne/dizi referanslarının tekrar oluşturulmasını önler. Özetle, React.memo bileşenleri, useMemo değerleri optimize eder.

  2. Uygulamamın paket boyutunu nasıl kontrol edebilirim?

    Webpack Bundle Analyzer gibi araçları kullanarak uygulamanızın nihai paketinin (bundle) içeriğini görsel olarak inceleyebilirsiniz. Bu araçlar, hangi kütüphanelerin veya modüllerin pakette en çok yer kapladığını gösterir, böylece optimizasyon için hedef alanları belirleyebilirsiniz. Ayrıca, tarayıcınızın geliştirici araçlarındaki “Network” sekmesinden uygulamanızın toplam JavaScript boyutunu ve yüklenme süresini görebilirsiniz.

  3. Veri çekme işlemleri için react-query veya SWR kullanmak neden önemlidir?

    react-query ve SWR gibi kütüphaneler, sadece veri çekme işlemini basitleştirmekle kalmaz, aynı zamanda gelişmiş önbellekleme, otomatik yeniden doğrulama, arka plan güncellemeleri, senkronizasyon ve hata yönetimi gibi özellikler sunar. Bu özellikler, uygulamanızın ağ isteklerini daha verimli hale getirerek kullanıcıya daha hızlı ve güncel veriler sunulmasını sağlar. Manuel olarak bu karmaşık mekanizmaları uygulamak yerine, bu kütüphaneler sayesinde daha az kodla daha performanslı ve kararlı bir veri akışı elde edersiniz.

  4. Her bileşene React.memo eklemeli miyim?

    Hayır, her bileşene React.memo eklemek her zaman en iyi yaklaşım değildir. React.memo‘nun kendisi de özellik karşılaştırması yapmak için bir miktar maliyet getirir. Bu maliyet, bileşenin render maliyetinden daha yüksekse, performans düşüşüne neden olabilir. React.memo‘yu genellikle büyük, karmaşık veya sık sık yeniden render edilen ancak prop’ları nadiren değişen bileşenlerde kullanmalısınız. Performans darboğazlarını React DevTools ile tespit ettikten sonra hedefli optimizasyon yapmak daha verimli olacaktır.

#React #Performans #WebGeliştirme #Frontend #Optimizasyon

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.