Türkçe

React state yönetimi için kapsamlı bir rehber. useState, Context API, useReducer ve Redux, Zustand, TanStack Query gibi popüler kütüphaneleri keşfedin.

React State Yönetiminde Uzmanlaşma: Küresel Geliştirici Rehberi

Front-end geliştirme dünyasında, state yönetimi en kritik zorluklardan biridir. React kullanan geliştiriciler için bu zorluk, basit bir bileşen seviyesi endişesinden, bir uygulamanın ölçeklenebilirliğini, performansını ve sürdürülebilirliğini tanımlayabilen karmaşık bir mimari karara dönüştü. İster Singapur'da tek başına çalışan bir geliştirici, ister Avrupa'ya yayılmış dağıtık bir ekibin parçası, ister Brezilya'da bir startup kurucusu olun, React state yönetiminin genel yapısını anlamak, sağlam ve profesyonel uygulamalar oluşturmak için esastır.

Bu kapsamlı rehber, React'in yerleşik araçlarından güçlü harici kütüphanelere kadar, state yönetiminin tüm spektrumunda size yol gösterecektir. Her yaklaşımın arkasındaki 'neden'i keşfedecek, pratik kod örnekleri sunacak ve dünyanın neresinde olursanız olun projeniz için doğru aracı seçmenize yardımcı olacak bir karar çerçevesi sunacağız.

React'te 'State' Nedir ve Neden Bu Kadar Önemlidir?

Araçlara dalmadan önce, 'state' kavramı hakkında net ve evrensel bir anlayış oluşturalım. Özünde, state, uygulamanızın belirli bir andaki durumunu tanımlayan herhangi bir veridir. Bu herhangi bir şey olabilir:

React, kullanıcı arayüzünün state'in bir fonksiyonu olduğu ilkesi üzerine kurulmuştur (UI = f(state)). State değiştiğinde, React bu değişikliği yansıtmak için kullanıcı arayüzünün gerekli kısımlarını verimli bir şekilde yeniden render eder. Zorluk, bu state'in bileşen ağacında doğrudan ilişkili olmayan birden fazla bileşen tarafından paylaşılması ve değiştirilmesi gerektiğinde ortaya çıkar. İşte bu noktada state yönetimi, kritik bir mimari endişe haline gelir.

Temel: useState ile Yerel (Local) State

Her React geliştiricisinin yolculuğu useState hook'u ile başlar. Bu, tek bir bileşene özgü (yerel) bir state parçası beyan etmenin en basit yoludur.

Örneğin, basit bir sayacın state'ini yönetmek:


import React, { useState } from 'react';

function Counter() {
  // 'count' state değişkenidir
  // 'setCount' ise onu güncelleyen fonksiyondur
  const [count, setCount] = useState(0);

  return (
    

Butona {count} kez tıkladınız

); }

useState, form girdileri, açma/kapama anahtarları veya durumu uygulamanın diğer bölümlerini etkilemeyen herhangi bir kullanıcı arayüzü öğesi gibi paylaşılması gerekmeyen state'ler için mükemmeldir. Sorun, başka bir bileşenin `count` değerini bilmesi gerektiğinde başlar.

Klasik Yaklaşım: State'i Yukarı Taşıma ve Prop Drilling

React'te bileşenler arasında state paylaşmanın geleneksel yolu, onu en yakın ortak atalarına "yukarı taşımaktır". State daha sonra props aracılığıyla alt bileşenlere akar. Bu, temel ve önemli bir React desenidir.

Ancak, uygulamalar büyüdükçe bu durum "prop drilling" olarak bilinen bir soruna yol açabilir. Bu, bir prop'u, veriye aslında ihtiyaç duymayan birden çok ara bileşen katmanı üzerinden, yalnızca veriye ihtiyaç duyan derinde yuvalanmış bir alt bileşene ulaştırmak için geçirmek zorunda kaldığınızda meydana gelir. Bu, kodun okunmasını, yeniden düzenlenmesini ve bakımını zorlaştırabilir.

Bir kullanıcının tema tercihinin (örneğin, 'dark' veya 'light') bileşen ağacının derinliklerindeki bir düğme tarafından erişilmesi gerektiğini hayal edin. Bunu şu şekilde geçirmeniz gerekebilir: Uygulama -> Düzen -> Sayfa -> Başlık -> TemaDeğiştirmeDüğmesi. Bu prop'u yalnızca Uygulama (state'in tanımlandığı yer) ve TemaDeğiştirmeDüğmesi (kullanıldığı yer) umursar, ancak Düzen, Sayfa ve Başlık aracılık yapmak zorunda kalır. İşte daha gelişmiş state yönetimi çözümlerinin çözmeyi amaçladığı sorun budur.

React'in Yerleşik Çözümleri: Context ve Reducer'ların Gücü

Prop drilling zorluğunun farkında olan React ekibi, Context API'yi ve useReducer hook'unu tanıttı. Bunlar, harici bağımlılıklar eklemeden önemli sayıda state yönetimi senaryosunu halledebilen güçlü, yerleşik araçlardır.

1. Context API: State'i Küresel Olarak Yayınlama

Context API, verileri bileşen ağacında her seviyede manuel olarak props geçmek zorunda kalmadan iletmenin bir yolunu sunar. Bunu, uygulamanızın belirli bir bölümü için küresel bir veri deposu olarak düşünebilirsiniz.

Context kullanımı üç ana adımı içerir:

  1. Context'i Oluşturun: Bir context nesnesi oluşturmak için `React.createContext()` kullanın.
  2. Context'i Sağlayın: Bileşen ağacınızın bir bölümünü sarmak ve ona bir `value` geçirmek için `Context.Provider` bileşenini kullanın. Bu sağlayıcı içindeki herhangi bir bileşen bu değere erişebilir.
  3. Context'i Tüketin: Context'e abone olmak ve mevcut değerini almak için bir bileşen içinde `useContext` hook'unu kullanın.

Örnek: Context kullanarak basit bir tema değiştirici


// 1. Context'i oluşturun (örneğin, theme-context.js dosyasında)
import { createContext, useState } from 'react';

export const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

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

  // value nesnesi, tüm tüketici bileşenler tarafından kullanılabilir olacak
  const value = { theme, toggleTheme };

  return (
    
      {children}
    
  );
}

// 2. Context'i sağlayın (örneğin, ana App.js dosyanızda)
import { ThemeProvider } from './theme-context';
import MyPage from './MyPage';

function App() {
  return (
    
      
    
  );
}

// 3. Context'i tüketin (örneğin, derinde yuvalanmış bir bileşende)
import { useContext } from 'react';
import { ThemeContext } from './theme-context';

function ThemeToggleButton() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    
  );
}

Context API'nin Artıları:

Eksileri ve Performans Değerlendirmeleri:

2. useReducer Hook'u: Öngörülebilir State Geçişleri İçin

useState basit state'ler için harikayken, useReducer daha karmaşık state mantığını yönetmek için tasarlanmış daha güçlü kardeşidir. Özellikle birden çok alt değer içeren veya bir sonraki state'in bir öncekine bağlı olduğu durumlarda kullanışlıdır.

Redux'tan esinlenen useReducer, bir `reducer` fonksiyonu ve bir `dispatch` fonksiyonu içerir:

Örnek: Artırma, azaltma ve sıfırlama eylemlerine sahip bir sayaç


import React, { useReducer } from 'react';

// 1. Başlangıç state'ini tanımlayın
const initialState = { count: 0 };

// 2. Reducer fonksiyonunu oluşturun
function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return initialState;
    default:
      throw new Error('Beklenmedik eylem türü');
  }
}

function ReducerCounter() {
  // 3. useReducer'ı başlatın
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      

Sayı: {state.count}

{/* 4. Kullanıcı etkileşiminde eylemleri dispatch edin */} ); }

useReducer kullanmak, state güncelleme mantığınızı tek bir yerde (reducer fonksiyonunda) merkezileştirir, bu da onu daha öngörülebilir, test edilmesi daha kolay ve özellikle mantık karmaşıklığı arttıkça daha sürdürülebilir hale getirir.

Güçlü İkili: useContext + useReducer

React'in yerleşik hook'larının gerçek gücü, useContext ve useReducer'ı birleştirdiğinizde ortaya çıkar. Bu desen, herhangi bir harici bağımlılık olmadan sağlam, Redux benzeri bir state yönetimi çözümü oluşturmanıza olanak tanır.

Bu desen harikadır çünkü `dispatch` fonksiyonunun kendisi kararlı bir kimliğe sahiptir ve yeniden render'lar arasında değişmez. Bu, yalnızca eylemleri `dispatch` etmesi gereken bileşenlerin, state değeri değiştiğinde gereksiz yere yeniden render edilmeyeceği anlamına gelir ve yerleşik bir performans optimizasyonu sağlar.

Örnek: Basit bir alışveriş sepetini yönetme


// 1. cart-context.js içinde kurulum
import { createContext, useReducer, useContext } from 'react';

const CartStateContext = createContext();
const CartDispatchContext = createContext();

const cartReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_ITEM':
      // Bir öğe ekleme mantığı
      return [...state, action.payload];
    case 'REMOVE_ITEM':
      // id'ye göre bir öğeyi kaldırma mantığı
      return state.filter(item => item.id !== action.payload.id);
    default:
      throw new Error(`Bilinmeyen eylem: ${action.type}`);
  }
};

export const CartProvider = ({ children }) => {
  const [state, dispatch] = useReducer(cartReducer, []);

  return (
    
      
        {children}
      
    
  );
};

// Kolay tüketim için özel hook'lar
export const useCart = () => useContext(CartStateContext);
export const useCartDispatch = () => useContext(CartDispatchContext);

// 2. Bileşenlerde kullanım
// ProductComponent.js - yalnızca bir eylem dispatch etmesi gerekiyor
function ProductComponent({ product }) {
  const dispatch = useCartDispatch();
  
  const handleAddToCart = () => {
    dispatch({ type: 'ADD_ITEM', payload: product });
  };

  return ;
}

// CartDisplayComponent.js - yalnızca state'i okuması gerekiyor
function CartDisplayComponent() {
  const cartItems = useCart();

  return 
Sepetteki Ürünler: {cartItems.length}
; }

State ve dispatch'i iki ayrı context'e bölerek bir performans avantajı elde ederiz: Yalnızca eylem gönderen `ProductComponent` gibi bileşenler, sepetin state'i değiştiğinde yeniden render edilmez.

Harici Kütüphanelere Ne Zaman Başvurulmalı?

useContext + useReducer deseni güçlüdür, ancak her derde deva değildir. Uygulamalar ölçeklendikçe, özel harici kütüphaneler tarafından daha iyi hizmet verilen ihtiyaçlarla karşılaşabilirsiniz. Şu durumlarda harici bir kütüphane düşünmelisiniz:

Popüler State Yönetim Kütüphaneleriyle Küresel Bir Tur

React ekosistemi, her birinin kendi felsefesi ve ödünleşimleri olan geniş bir state yönetimi çözümleri yelpazesi sunan canlı bir yapıya sahiptir. Dünya çapındaki geliştiriciler için en popüler seçeneklerden bazılarını keşfedelim.

1. Redux (& Redux Toolkit): Yerleşik Standart

Redux, yıllardır baskın state yönetimi kütüphanesi olmuştur. State değişikliklerini öngörülebilir ve izlenebilir kılan katı bir tek yönlü veri akışını zorunlu kılar. İlk zamanlardaki Redux, boilerplate (tekrar eden standart kod) miktarıyla bilinirken, Redux Toolkit (RTK) kullanan modern yaklaşım süreci önemli ölçüde basitleştirmiştir.

2. Zustand: Minimalist ve Dayatmacı Olmayan Seçenek

Almancada "durum" (state) anlamına gelen Zustand, minimalist ve esnek bir yaklaşım sunar. Genellikle Redux'a daha basit bir alternatif olarak görülür ve boilerplate olmadan merkezi bir store'un avantajlarını sağlar.


// store.js
import { create } from 'zustand';

const useBearStore = create((set) => ({
  bears: 0,
  increasePopulation: () => set((state) => ({ bears: state.bears + 1 })),
  removeAllBears: () => set({ bears: 0 }),
}));

// MyComponent.js
function BearCounter() {
  const bears = useBearStore((state) => state.bears);
  return 

Buralarda {bears} ayı var...

; } function Controls() { const increasePopulation = useBearStore((state) => state.increasePopulation); return ; }

3. Jotai & Recoil: Atomik Yaklaşım

Jotai ve Recoil (Facebook'tan), "atomik" state yönetimi kavramını popüler hale getirmiştir. Tek bir büyük state nesnesi yerine, state'inizi "atom" adı verilen küçük, bağımsız parçalara ayırırsınız.

4. TanStack Query (önceden React Query): Sunucu Tarafı State'inin Kralı

Belki de son yıllardaki en önemli paradigma kayması, "state" olarak adlandırdığımız şeyin çoğunun aslında sunucu state'i olduğunun farkına varılmasıdır — yani bir sunucuda yaşayan ve istemci uygulamamızda çekilen, önbelleğe alınan ve senkronize edilen veriler. TanStack Query genel amaçlı bir state yöneticisi değildir; sunucu state'ini yönetmek için özel bir araçtır ve bunu olağanüstü iyi yapar.

Doğru Seçimi Yapmak: Bir Karar Çerçevesi

Bir state yönetimi çözümü seçmek bunaltıcı gelebilir. İşte seçiminize rehberlik edecek pratik, küresel olarak uygulanabilir bir karar çerçevesi. Kendinize bu soruları sırayla sorun:

  1. State gerçekten küresel mi, yoksa yerel olabilir mi?
    Her zaman useState ile başlayın. Kesinlikle gerekli olmadıkça küresel state kullanmayın.
  2. Yönettiğiniz veri aslında sunucu state'i mi?
    Eğer bir API'den gelen veriyse, TanStack Query kullanın. Bu, önbellekleme, veri çekme ve senkronizasyonu sizin için halleder. Muhtemelen uygulamanızın "state"inin %80'ini yönetecektir.
  3. Kalan UI state'i için sadece prop drilling'den kaçınmanız mı gerekiyor?
    Eğer state sık güncellenmiyorsa (örneğin, tema, kullanıcı bilgisi, dil), yerleşik Context API mükemmel, bağımlılıksız bir çözümdür.
  4. UI state mantığınız karmaşık ve öngörülebilir geçişlere mi sahip?
    useReducer'ı Context ile birleştirin. Bu size harici kütüphaneler olmadan state mantığını yönetmek için güçlü, organize bir yol sunar.
  5. Context ile performans sorunları mı yaşıyorsunuz veya state'iniz birçok bağımsız parçadan mı oluşuyor?
    Jotai gibi atomik bir state yöneticisi düşünün. Gereksiz yeniden render'ları önleyerek mükemmel performansa sahip basit bir API sunar.
  6. Katı, öngörülebilir bir mimari, middleware ve güçlü hata ayıklama araçları gerektiren büyük ölçekli bir kurumsal uygulama mı geliştiriyorsunuz?
    Bu, Redux Toolkit için en uygun kullanım durumudur. Yapısı ve ekosistemi, büyük ekiplerde karmaşıklık ve uzun vadeli sürdürülebilirlik için tasarlanmıştır.

Özet Karşılaştırma Tablosu

Çözüm En Uygun Olduğu Alan Temel Avantajı Öğrenme Eğrisi
useState Yerel bileşen state'i Basit, yerleşik Çok Düşük
Context API Düşük frekanslı küresel state (tema, kimlik doğrulama) Prop drilling'i çözer, yerleşik Düşük
useReducer + Context Harici kütüphanesiz karmaşık UI state'i Organize mantık, yerleşik Orta
TanStack Query Sunucu state'i (API veri önbellekleme/senkronizasyon) Büyük miktarda state mantığını ortadan kaldırır Orta
Zustand / Jotai Basit küresel state, performans optimizasyonu Minimal boilerplate, harika performans Düşük
Redux Toolkit Karmaşık, paylaşılan state'e sahip büyük ölçekli uygulamalar Öngörülebilirlik, güçlü geliştirici araçları, ekosistem Yüksek

Sonuç: Pragmatik ve Küresel Bir Bakış Açısı

React state yönetimi dünyası artık bir kütüphanenin diğerine karşı savaşı değil. Farklı araçların farklı sorunları çözmek için tasarlandığı sofistike bir manzaraya dönüştü. Modern, pragmatik yaklaşım, ödünleşimleri anlamak ve uygulamanız için bir 'state yönetimi araç kiti' oluşturmaktır.

Dünya genelindeki çoğu proje için güçlü ve etkili bir yığın şu şekilde başlar:

  1. Tüm sunucu state'i için TanStack Query.
  2. Paylaşılmayan, basit tüm UI state'i için useState.
  3. Basit, düşük frekanslı küresel UI state'i için useContext.

Ancak bu araçlar yetersiz kaldığında Jotai, Zustand veya Redux Toolkit gibi özel bir küresel state kütüphanesine başvurmalısınız. Sunucu state'i ile istemci state'i arasında net bir ayrım yaparak ve önce en basit çözümle başlayarak, ekibinizin büyüklüğü veya kullanıcılarınızın konumu ne olursa olsun, performanslı, ölçeklenebilir ve bakımı keyifli uygulamalar oluşturabilirsiniz.