React ve React Native İçin Ortak State Yönetimi: Tek Kod Tabanında Uyumlu ve Ölçeklenebilir Çözümler

An abstract illustration depicting interconnected nodes and data flow, symbolizing unified state management across React and React Native applications from a single codebase.

Modern yazılım geliştirme dünyasında, kullanıcı deneyiminin sınırları sürekli genişliyor. Web uygulamalarımızın yanı sıra mobil uygulamalar da vazgeçilmez bir hal aldı. Birçok proje hem web hem de mobil platformda (React ve React Native ile) aynı işlevselliği sunmayı hedeflerken, geliştiriciler olarak karşılaştığımız en büyük zorluklardan biri, her iki platformda da tutarlı ve verimli bir state yönetimi sağlamak oluyor.

Benim geliştirme tecrübelerimde, genellikle ayrı ayrı yönetilen web ve mobil state'lerinin hem geliştirme süresini uzattığını hem de hatalara davetiye çıkardığını gözlemledim. Peki ya bu iki dünyayı, aynı iş mantığını ve veri akışını paylaşan, tek bir uyumlu ve ölçeklenebilir state yönetim sistemiyle birleştirmek mümkün olsaydı? İşte bu yazıda, React ve React Native projelerinizde ortak state yönetimi yaklaşımlarını, temel prensiplerini ve pratik çözümlerini derinlemesine inceleyeceğiz. Amacımız, tek bir kod tabanında (monorepo gibi) web ve mobil uygulamalarınızı güçlendiren stratejiler sunmak.

Neden Ortak State Yönetimi? Tek Kod Tabanının Avantajları

Web ve mobil uygulamalarınız için paylaşılan bir state yönetim stratejisi benimsemek, sadece ‘daha az kod yazmak’tan çok daha fazlasını ifade eder. Bu yaklaşımın sunduğu temel avantajlar şunlardır:

  • Kod Tekrarını Azaltma: İş mantığı, veri alma (fetching) ve state güncelleme gibi kritik operasyonları bir kez yazıp hem web hem de mobil platformda kullanmak, kod tekrarını önemli ölçüde azaltır.

  • Tutarlılık ve Güvenilirlik: Her iki platformda da aynı state yönetim mantığını kullanmak, uygulamanızın davranışında ve veri akışında tutarlılık sağlar. Bu da kullanıcı deneyimini iyileştirir ve hataları en aza indirir.

  • Daha Hızlı Geliştirme: Ortak bir yapı üzerinde çalışmak, yeni özelliklerin her iki platforma da daha hızlı entegre edilmesini sağlar.

  • Kolay Bakım: State yönetimiyle ilgili değişiklikler veya hata düzeltmeleri tek bir merkezi yerden yapılabildiği için bakım maliyetleri düşer.

  • Ölçeklenebilirlik: Özellikle monorepo yapılarında, paylaşılan paketler ve modüller aracılığıyla bu strateji, uygulamanızın büyümesiyle ortaya çıkabilecek karmaşıklığı yönetmeye yardımcı olur.

Ortak State Yönetiminin Zorlukları

Avantajlarının yanı sıra, web ve mobil için ortak bir state yönetim çözümü tasarlarken dikkat etmemiz gereken bazı zorluklar da vardır:

  • Platform Spesifik Farklılıklar: Web'de localStorage, mobil'de AsyncStorage gibi platforma özgü depolama mekanizmaları veya Native Modüllerle (React Native özelinde) etkileşim gibi durumlar, soyutlama gerektirir.

  • Performans Kısıtlamaları: Özellikle mobil cihazlarda daha kısıtlı kaynaklar olduğundan, state yönetim çözümünün performans üzerindeki etkisi daha dikkatli değerlendirilmelidir. Büyük state ağaçları veya sık güncellemeler mobil performansı olumsuz etkileyebilir. React Native performans optimizasyonları bu noktada daha da önem kazanır.

  • Bundle Boyutu: Gereksiz bağımlılıklar veya kod şişkinliği, özellikle mobil uygulamalarda başlangıç süresini ve uygulama boyutunu artırabilir. Paylaşılan kodun ne kadarının her iki platform için de gerçekten gerekli olduğunu iyi belirlemek gerekir.

Complex network diagram illustrating interconnected data systems, symbolizing the challenges of managing shared state across React and React Native applications.

Çözüm Stratejileri: Ortak State'i Nasıl Yönetiriz?

Şimdi gelelim bu zorlukların üstesinden gelerek ortak state yönetimini nasıl uygulayabileceğimize dair pratik stratejilere:

1. Context API ve Custom Hooks ile Temel Çözümler

React'ın kendi bünyesinde sunduğu Context API ve Custom Hooks, daha basit veya orta ölçekli uygulamalar için mükemmel bir başlangıç noktasıdır. State mantığını soyutlamak ve bileşenler arasında paylaşmak için idealdirler.

Ortak bir Custom Hook Oluşturma:

// shared/hooks/useAuth.js
import { useState, useEffect } from 'react';

// Platforma özgü depolama mekanizmasını soyutlama
const storage = typeof window !== 'undefined' ? localStorage : null; // Web için localStorage

const useAuth = () => {
  const [user, setUser] = useState(null);
  const [token, setToken] = useState(null);

  useEffect(() => {
    if (storage) {
      const storedToken = storage.getItem('authToken');
      if (storedToken) {
        setToken(storedToken);
        // Kullanıcı bilgisini token'dan çözümle veya API'dan çek
        setUser({ name: 'İsmail YAĞCI', email: 'ismailyagci371@gmail.com' });
      }
    }
  }, []);

  const login = (email, password) => {
    // API çağrısı ile kimlik doğrulama
    const fetchedToken = 'fake-jwt-token'; // Gerçek uygulamada API'dan gelecek
    setToken(fetchedToken);
    setUser({ name: 'İsmail YAĞCI', email: email });
    if (storage) {
      storage.setItem('authToken', fetchedToken);
    }
  };

  const logout = () => {
    setToken(null);
    setUser(null);
    if (storage) {
      storage.removeItem('authToken');
    }
  };

  return { user, token, login, logout };
};

export default useAuth;

Bu örnekte, useAuth hook'u hem web (localStorage) hem de mobil (burada AsyncStorage yerine geçecek bir soyutlama veya React Native tarafında AsyncStorage'ı doğrudan kullanan farklı bir implementasyon ile) çalışabilecek bir iskelet sunar. typeof window !== 'undefined' kontrolü, platforma özgü API'leri koşullu olarak kullanmanın basit bir yoludur.

Daha detaylı Custom Hooks kullanımı ve Context API'nin gücü ile ilgili yazılarıma göz atabilirsiniz.

2. Platform Agnostik State Yönetimi Kütüphaneleri

Daha karmaşık ve büyük ölçekli uygulamalar için, Recoil, Zustand, Jotai veya Redux Toolkit gibi kütüphaneler, platformdan bağımsız (platform-agnostic) state yönetimi sağlamak için çok daha güçlü ve yapılandırılmış çözümler sunar.

Zustand ile Ortak Bir Store Oluşturma:

Zustand, minimal API'si ve hook tabanlı yaklaşımıyla hem React hem de React Native'de kolayca kullanılabilir. İş mantığını içeren store dosyalarınızı paylaşılan bir dizinde tutabilirsiniz.

// shared/stores/authStore.js
import { create } from 'zustand';

// Platforma özgü depolama için soyutlama (örneğin bir interface)
// Gerçek uygulamada, web ve mobil için farklı 'persistence' modülleri enjekte edilebilir.
const getStorage = () => {
  if (typeof window !== 'undefined' && window.localStorage) {
    return localStorage;
  } /*else if (isReactNative) { // React Native için özel kontrol ve AsyncStorage import'u
    return require('@react-native-async-storage/async-storage').default;
  }*/
  return null;
};

const storage = getStorage();

const useAuthStore = create((set) => ({
  user: null,
  token: null,
  login: async (email, password) => {
    // API çağrısı ile kimlik doğrulama
    const fetchedToken = 'mock-jwt-token'; // Gerçek API'dan gelecek
    const user = { name: 'İsmail YAĞCI', email: email };
    set({ user, token: fetchedToken });
    if (storage) {
      await storage.setItem('authToken', fetchedToken);
      // await storage.setItem('user', JSON.stringify(user));
    }
  },
  logout: async () => {
    set({ user: null, token: null });
    if (storage) {
      await storage.removeItem('authToken');
      // await storage.removeItem('user');
    }
  },
  initialize: async () => {
    if (storage) {
      const storedToken = await storage.getItem('authToken');
      if (storedToken) {
        set({ token: storedToken, user: { name: 'İsmail YAĞCI', email: 'ismailyagci371@gmail.com' } });
      }
    }
  }
}));

// Uygulama başlangıcında initialize çağrısı
useAuthStore.getState().initialize();

export default useAuthStore;

Bu store'u hem React web uygulamanızda hem de React Native mobil uygulamanızda aynı şekilde kullanabilirsiniz:

// React Component (Web veya Native)
import useAuthStore from './shared/stores/authStore';

function AuthDisplay() {
  const { user, token, login, logout } = useAuthStore();

  if (user) {
    return (
      <div>
        <p>Hoş geldin, {user.name}!</p>
        <button onClick={logout}>Çıkış Yap</button>
      </div>
    );
  }

  return (<button onClick={() => login('test@example.com', 'password')}>Giriş Yap</button>);
}

Recoil ve Zustand ile modern state yönetimi hakkındaki yazım, bu kütüphanelerin daha derinlemesine incelenmesine yardımcı olabilir.

Illustrative diagram of platform-agnostic state management, depicting a single state source feeding multiple application platforms such as web and mobile, relevant for React and React Native.

3. Soyutlama Katmanları ve Bağımlılık Enjeksiyonu

Daha karmaşık platform spesifik bağımlılıkları yönetmek için, soyutlama katmanları oluşturmak ve Bağımlılık Enjeksiyonu (Dependency Injection - DI) prensibini kullanmak oldukça etkilidir. Bu, iş mantığı katmanınızın belirli bir depolama mekanizmasına veya platform API'sine doğrudan bağımlı olmamasını sağlar.

Örnek: Platforma Özgü Servis Enjeksiyonu

// shared/services/authService.js
// Bu servis, platforma özgü 'storage' nesnesini dışarıdan alır.
class AuthService {
  constructor(storage) {
    this.storage = storage;
  }

  async login(email, password) {
    const fetchedToken = 'mock-jwt-token';
    await this.storage.setItem('authToken', fetchedToken);
    return { user: { name: 'İsmail YAĞCI', email: email }, token: fetchedToken };
  }

  async logout() {
    await this.storage.removeItem('authToken');
  }

  async getToken() {
    return this.storage.getItem('authToken');
  }
}

export default AuthService;
// web/utils/localStorageAdapter.js
const localStorageAdapter = {
  getItem: (key) => localStorage.getItem(key),
  setItem: (key, value) => localStorage.setItem(key, value),
  removeItem: (key) => localStorage.removeItem(key),
};
export default localStorageAdapter;
// mobile/utils/asyncStorageAdapter.js (React Native)
import AsyncStorage from '@react-native-async-storage/async-storage';

const asyncStorageAdapter = {
  getItem: async (key) => await AsyncStorage.getItem(key),
  setItem: async (key, value) => await AsyncStorage.setItem(key, value),
  removeItem: async (key) => await AsyncStorage.removeItem(key),
};
export default asyncStorageAdapter;
// web/index.js veya mobile/App.js
import AuthService from '../shared/services/authService';
import localStorageAdapter from './utils/localStorageAdapter'; // Web için
// import asyncStorageAdapter from './mobile/utils/asyncStorageAdapter'; // Mobil için

// Web uygulamasında
const webAuthService = new AuthService(localStorageAdapter);

// Mobil uygulamasında (farklı bir yerden)
// const mobileAuthService = new AuthService(asyncStorageAdapter);

// Daha sonra bu servis kullanılabilir.

Bu yaklaşım, iş mantığını depolama mekanizmalarından tamamen ayırarak esneklik ve test edilebilirliği artırır. React'te Bağımlılık Enjeksiyonu ve Node.js'te Bağımlılık Enjeksiyonu yazılarım bu konuda size daha fazla fikir verebilir.

4. Veri Senkronizasyonu ve Çevrimdışı Destek

Özellikle mobil uygulamalarda çevrimdışı çalışma yeteneği kritik öneme sahiptir. Ortak bir state yönetim sistemi tasarlarken, verilerin nasıl senkronize edildiği ve çevrimdışı durumlarda nasıl yönetildiği önemlidir. React Native'da çevrimdışı destek ve veri senkronizasyonu üzerine yazdığım detaylı rehberime göz atarak bu konuda daha fazla bilgi edinebilirsiniz.

React Native'da yerel depolama çözümleri makalesi de platforma özgü depolama stratejileri için faydalı olacaktır.

Ortak State Yönetimi İçin En İyi Uygulamalar

  • Modüler Yapı: State'i mantıksal modüllere ayırın (örneğin, authStore.js, productsStore.js). Bu, kodun daha düzenli olmasını ve yalnızca ilgili kısımların yüklenmesini sağlar. Modüler mimari her zaman anahtar.

  • TypeScript Kullanımı: State yapınızı ve eylemlerinizi TypeScript ile tanımlayarak daha güvenli ve hata ayıklaması kolay bir kod tabanı oluşturun. TypeScript'in gücü, özellikle büyük projelerde vazgeçilmezdir.

  • Performansı Göz Önünde Bulundurma: Özellikle mobil platformlar için, gereksiz yeniden render'ları önlemek amacıyla memoization (React.memo, useCallback, useMemo) tekniklerini kullanın.

  • Kapsamlı Testler: Paylaşılan state mantığınızın hem web hem de mobil ortamda beklenen şekilde çalıştığından emin olmak için kapsamlı bir test stratejisi uygulayın. React uygulamalarında test stratejileri bu konuda yol göstericidir.

  • Doğru Kütüphane Seçimi: Projenizin ölçeği ve karmaşıklığına uygun bir state yönetim kütüphanesi seçmek kritik öneme sahiptir. Bazen Context API yeterliyken, bazen Redux Toolkit veya Zustand gibi daha güçlü çözümlere ihtiyaç duyulur. React'te ileri seviye state yönetimi yazım bu konuda size yardımcı olabilir.

Sonuç

React ve React Native için ortak state yönetimi, modern web ve mobil uygulama geliştirmenin temel taşlarından biridir. Tek bir kod tabanında tutarlı, verimli ve ölçeklenebilir çözümler üretmek, geliştirme sürecinizi hızlandırırken, kullanıcılarınıza da kesintisiz bir deneyim sunmanızı sağlar. İster Context API ve Custom Hooks ile başlayan basit bir yaklaşım benimseyin, ister Zustand veya Redux Toolkit gibi güçlü kütüphanelerle karmaşık state'leri yönetin; önemli olan, platforma özgü farklılıkları soyutlayarak iş mantığınızı merkezileştirmektir.

Unutmayın, en iyi çözüm projenizin özel ihtiyaçlarına göre değişir. Bu yazıda ele aldığımız stratejiler ve en iyi uygulamalar, sizi bu yolda doğru adımları atmaya teşvik edecektir. Eğer aklınıza takılan sorular olursa veya bu konular hakkında daha fazla bilgi almak isterseniz, bana ismailyagci371@gmail.com adresinden veya sosyal medya kanallarından ulaşabilirsiniz. Sağlıklı ve başarılı kodlamalar dilerim!

Orijinal yazı: https://ismailyagci.com/articles/react-ve-react-native-icin-ortak-state-yonetimi-tek-kod-tabaninda-uyumlu-ve-olceklenebilir-cozumler

Yorumlar

Bu blogdaki popüler yayınlar

Node.js ile Ölçeklenebilir Mikroservisler: Adım Adım Bir Mimari Kılavuzu

JavaScript ve Node.js'te Tasarım Desenleri: Uygulamanızı Güçlendirin ve Ölçeklendirin

Anlık Etkileşim: Node.js, WebSockets ve Socket.IO ile Gerçek Zamanlı Uygulama Geliştirme Rehberi