Türkçe

Uygulamalarınızda durumu etkili bir şekilde yönetmek, performansı optimize etmek ve gereksiz yeniden render'ları önlemek için gelişmiş React Context Sağlayıcı desenlerini keşfedin.

React Context Sağlayıcı Desenleri: Performansı Optimize Etme ve Yeniden Render Sorunlarından Kaçınma

React Context API, uygulamalarınızda genel durumu yönetmek için güçlü bir araçtır. Her seviyede manuel olarak prop'lar geçirmek zorunda kalmadan bileşenler arasında veri paylaşmanızı sağlar. Ancak, Context'i yanlış kullanmak performans sorunlarına, özellikle de gereksiz yeniden render'lara yol açabilir. Bu makale, performansı optimize etmenize ve bu tuzaklardan kaçınmanıza yardımcı olan çeşitli Context Sağlayıcı desenlerini incelemektedir.

Sorunu Anlamak: Gereksiz Yeniden Render'lar

Varsayılan olarak, bir Context değeri değiştiğinde, o Context'i tüketen tüm bileşenler, Context'in değişen belirli bir kısmına bağlı olmasalar bile yeniden render edilir. Bu, özellikle büyük ve karmaşık uygulamalarda önemli bir performans darboğazı olabilir. Kullanıcı bilgileri, tema ayarları ve uygulama tercihleri içeren bir Context'iniz olduğunu düşünün. Yalnızca tema ayarı değişirse, ideal olarak yalnızca temayla ilgili bileşenler yeniden render edilmeli, tüm uygulama değil.

Örneklemek gerekirse, birden fazla ülkede erişilebilen küresel bir e-ticaret uygulaması hayal edin. Para birimi tercihi değişirse (Context içinde ele alınır), tüm ürün kataloğunun yeniden render edilmesini istemezsiniz - yalnızca fiyat göstergelerinin güncellenmesi gerekir.

Desen 1: useMemo ile Değer Memorizasyonu

Gereksiz yeniden render'ları önlemenin en basit yolu, useMemo kullanarak Context değerini memorize etmektir. Bu, Context değerinin yalnızca bağımlılıkları değiştiğinde değişmesini sağlar.

Örnek:

Kullanıcı verilerini ve kullanıcının profilini güncelleme işlevini sağlayan bir `UserContext`'imiz olduğunu varsayalım.


import React, { createContext, useState, useMemo } from 'react';

const UserContext = createContext(null);

function UserProvider({ children }) {
  const [user, setUser] = useState({
    name: 'John Doe',
    email: 'john.doe@example.com',
    location: 'New York, USA'
  });

  const updateUser = (newUserData) => {
    setUser(prevState => ({ ...prevState, ...newUserData }));
  };

  const contextValue = useMemo(() => ({
    user,
    updateUser,
  }), [user, setUser]);

  return (
    
      {children}
    
  );
}

export { UserContext, UserProvider };

Bu örnekte, useMemo, `contextValue`'nun yalnızca `user` durumu veya `setUser` işlevi değiştiğinde değişmesini sağlar. İkisi de değişmezse, `UserContext`'i tüketen bileşenler yeniden render edilmez.

Faydaları:

Dezavantajları:

Desen 2: Birden Fazla Context ile İlgileri Ayırma

Daha ayrıntılı bir yaklaşım, Context'inizi her biri belirli bir durum parçasından sorumlu olan birden fazla, daha küçük Context'e bölmektir. Bu, yeniden render kapsamını azaltır ve bileşenlerin yalnızca bağlı oldukları belirli veriler değiştiğinde yeniden render edilmesini sağlar.

Örnek:

Tek bir `UserContext` yerine, kullanıcı verileri ve kullanıcı tercihleri için ayrı context'ler oluşturabiliriz.


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

const UserDataContext = createContext(null);
const UserPreferencesContext = createContext(null);

function UserDataProvider({ children }) {
  const [user, setUser] = useState({
    name: 'John Doe',
    email: 'john.doe@example.com',
    location: 'New York, USA'
  });

  const updateUser = (newUserData) => {
    setUser(prevState => ({ ...prevState, ...newUserData }));
  };

  return (
    
      {children}
    
  );
}

function UserPreferencesProvider({ children }) {
  const [theme, setTheme] = useState('light');
  const [language, setLanguage] = useState('en');

  const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    
      {children}
    
  );
}

export { UserDataContext, UserDataProvider, UserPreferencesContext, UserPreferencesProvider };

Artık, yalnızca kullanıcı verilerine ihtiyaç duyan bileşenler `UserDataContext`'i ve yalnızca tema ayarlarına ihtiyaç duyan bileşenler `UserPreferencesContext`'i tüketebilir. Tema değişiklikleri artık `UserDataContext`'i tüketen bileşenlerin yeniden render edilmesine neden olmaz ve bunun tersi de geçerlidir.

Faydaları:

Dezavantajları:

Desen 3: Özel Kancalarla Seçici İşlevler

Bu desen, Context değerinin belirli bölümlerini çıkaran ve yalnızca bu belirli bölümler değiştiğinde yeniden render eden özel kancalar oluşturmayı içerir. Bu, özellikle birçok özelliği olan büyük bir Context değeriniz olduğunda kullanışlıdır, ancak bir bileşenin yalnızca birkaçına ihtiyacı vardır.

Örnek:

Orijinal `UserContext`'i kullanarak, belirli kullanıcı özelliklerini seçmek için özel kancalar oluşturabiliriz.


import React, { useContext } from 'react';
import { UserContext } from './UserContext'; // UserContext'in UserContext.js içinde olduğunu varsayalım

function useUserName() {
  const { user } = useContext(UserContext);
  return user.name;
}

function useUserEmail() {
  const { user } = useContext(UserContext);
  return user.email;
}

export { useUserName, useUserEmail };

Artık bir bileşen, kullanıcının adı değiştiğinde yalnızca yeniden render etmek için `useUserName`'i ve kullanıcının e-postası değiştiğinde yalnızca yeniden render etmek için `useUserEmail`'i kullanabilir. Diğer kullanıcı özelliklerindeki (örneğin, konum) değişiklikler yeniden render'ları tetiklemeyecektir.


import React from 'react';
import { useUserName, useUserEmail } from './UserHooks';

function UserProfile() {
  const name = useUserName();
  const email = useUserEmail();

  return (
    

Name: {name}

Email: {email}

); }

Faydaları:

Dezavantajları:

Desen 4: React.memo ile Bileşen Memorizasyonu

React.memo, işlevsel bir bileşeni memorize eden daha yüksek dereceli bir bileşendir (HOC). Prop'ları değişmediyse bileşenin yeniden render edilmesini önler. Performansı daha da optimize etmek için bunu Context ile birleştirebilirsiniz.

Örnek:

Kullanıcının adını görüntüleyen bir bileşenimiz olduğunu varsayalım.


import React, { useContext } from 'react';
import { UserContext } from './UserContext';

function UserName() {
  const { user } = useContext(UserContext);
  return 

Name: {user.name}

; } export default React.memo(UserName);

`UserName`'i `React.memo` ile sarmalayarak, yalnızca `user` prop'u (Context aracılığıyla örtük olarak geçirilir) değişirse yeniden render edilecektir. Ancak, bu basit örnekte, `React.memo` tek başına yeniden render'ları önlemez çünkü tüm `user` nesnesi hala bir prop olarak geçirilir. Gerçekten etkili hale getirmek için, seçici işlevler veya ayrı context'ler ile birleştirmeniz gerekir.

Daha etkili bir örnek, `React.memo`'yu seçici işlevlerle birleştirir:


import React from 'react';
import { useUserName } from './UserHooks';

function UserName() {
  const name = useUserName();
  return 

Name: {name}

; } function areEqual(prevProps, nextProps) { // Özel karşılaştırma işlevi return prevProps.name === nextProps.name; } export default React.memo(UserName, areEqual);

Burada, `areEqual`, `name` prop'unun değişip değişmediğini kontrol eden özel bir karşılaştırma işlevidir. Değişmediyse, bileşen yeniden render edilmeyecektir.

Faydaları:

Dezavantajları:

Desen 5: Context ve Reducer'ları (useReducer) Birleştirme

Context'i useReducer ile birleştirmek, karmaşık durum mantığını yönetmenize ve yeniden render'ları optimize etmenize olanak tanır. useReducer, öngörülebilir bir durum yönetimi deseni sağlar ve Context aracılığıyla birden çok ayarlayıcı işlevi geçirme ihtiyacını azaltarak eylemlere göre durumu güncellemenize olanak tanır.

Örnek:


import React, { createContext, useReducer, useContext } from 'react';

const UserContext = createContext(null);

const initialState = {
  user: {
    name: 'John Doe',
    email: 'john.doe@example.com',
    location: 'New York, USA'
  },
  theme: 'light',
  language: 'en'
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'UPDATE_USER':
      return { ...state, user: { ...state.user, ...action.payload } };
    case 'TOGGLE_THEME':
      return { ...state, theme: state.theme === 'light' ? 'dark' : 'light' };
    case 'SET_LANGUAGE':
      return { ...state, language: action.payload };
    default:
      return state;
  }
};

function UserProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    
      {children}
    
  );
}

function useUserState() {
  const { state } = useContext(UserContext);
  return state.user;
}

function useUserDispatch() {
    const { dispatch } = useContext(UserContext);
    return dispatch;
}


export { UserContext, UserProvider, useUserState, useUserDispatch };

Artık bileşenler, özel kancalar kullanarak duruma erişebilir ve eylemleri gönderebilir. Örneğin:


import React from 'react';
import { useUserState, useUserDispatch } from './UserContext';

function UserProfile() {
  const user = useUserState();
  const dispatch = useUserDispatch();

  const handleUpdateName = (e) => {
    dispatch({ type: 'UPDATE_USER', payload: { name: e.target.value } });
  };

  return (
    

Name: {user.name}

); }

Bu desen, durum yönetimine daha yapılandırılmış bir yaklaşımı teşvik eder ve karmaşık Context mantığını basitleştirebilir.

Faydaları:

Dezavantajları:

Desen 6: İyimser Güncellemeler

İyimser güncellemeler, sunucu onaylamadan önce bile, bir eylemin başarılı olduğu gibi UI'yı hemen güncellemeyi içerir. Bu, özellikle yüksek gecikmeli durumlarda kullanıcı deneyimini önemli ölçüde iyileştirebilir. Ancak, olası hataların dikkatli bir şekilde ele alınmasını gerektirir.

Örnek:

Kullanıcıların gönderileri beğenebileceği bir uygulama hayal edin. İyimser bir güncelleme, kullanıcı beğen düğmesini tıkladığında beğenme sayısını hemen artırır ve ardından sunucu isteği başarısız olursa değişikliği geri alır.


import React, { useContext, useState } from 'react';
import { UserContext } from './UserContext';

function LikeButton({ postId }) {
  const { dispatch } = useContext(UserContext);
  const [isLiking, setIsLiking] = useState(false);

  const handleLike = async () => {
    setIsLiking(true);
    // Beğenme sayısını iyimser bir şekilde güncelleyin
    dispatch({ type: 'INCREMENT_LIKES', payload: { postId } });

    try {
      // Bir API çağrısını simüle edin
      await new Promise(resolve => setTimeout(resolve, 500));

      // API çağrısı başarılı olursa, hiçbir şey yapmayın (UI zaten güncellendi)
    } catch (error) {
      // API çağrısı başarısız olursa, iyimser güncellemeyi geri alın
      dispatch({ type: 'DECREMENT_LIKES', payload: { postId } });
      alert('Gönderiyi beğenme başarısız oldu. Lütfen tekrar deneyin.');
    } finally {
      setIsLiking(false);
    }
  };

  return (
    
  );
}

Bu örnekte, `INCREMENT_LIKES` eylemi hemen gönderilir ve ardından API çağrısı başarısız olursa geri alınır. Bu, daha duyarlı bir kullanıcı deneyimi sağlar.

Faydaları:

Dezavantajları:

Doğru Deseni Seçmek

En iyi Context Sağlayıcı deseni, uygulamanızın özel ihtiyaçlarına bağlıdır. Seçmenize yardımcı olacak bir özet:

Context Performansını Optimize Etmek İçin Ek İpuçları

Sonuç

React Context API güçlü bir araçtır, ancak performans sorunlarından kaçınmak için doğru kullanmak önemlidir. Bu makalede tartışılan Context Sağlayıcı desenlerini anlayarak ve uygulayarak, durumu etkili bir şekilde yönetebilir, performansı optimize edebilir ve daha verimli ve duyarlı React uygulamaları oluşturabilirsiniz. Özel ihtiyaçlarınızı analiz etmeyi ve uygulamanızın gereksinimlerine en uygun deseni seçmeyi unutmayın.

Küresel bir bakış açısını göz önünde bulundurarak, geliştiriciler ayrıca durum yönetimi çözümlerinin farklı saat dilimlerinde, para birimi biçimlerinde ve bölgesel veri gereksinimlerinde sorunsuz bir şekilde çalıştığından emin olmalıdır. Örneğin, bir Context içindeki bir tarih biçimlendirme işlevi, kullanıcının tercihine veya konumuna göre yerelleştirilmeli ve kullanıcının uygulamaya nereden eriştiğine bakılmaksızın tutarlı ve doğru tarih görüntüleri sağlanmalıdır.