React Hooks: Fonksiyonel Bileşenlerle Dinamik ve Etkileşimli Uygulamalar Geliştirin

React projelerinde fonksiyonel bileşenler giderek daha fazla tercih ediliyor. Ancak, bir fonksiyonel bileşene state eklemek veya uygulamanın veri değişikliklerine dinamik olarak tepki vermesini sağlamak gerektiğinde işler karmaşıklaşabilir. İşte burada React Hooks devreye girmektedir.

Hooks, fonksiyonel bileşenlerin yeteneklerini genişleten ve bileşenlerin daha güçlü bir şekilde kullanılmasını sağlayan işlevlerdir. Hooks sayesinde, bir sınıf bileşene ihtiyaç duymadan “state” yönetebilir, veri değişikliklerine “tepki verebilir” ve bileşen “yaşam döngüsü” ile ilgili işlemleri gerçekleştirebilirsiniz.

React’in sunduğu bazı Hooks metotları şunlardır:

  • useState(): Bileşen içinde durum yönetimini sağlar.
  • useEffect(): Bileşen oluşturulduğunda, güncellendiğinde veya yok edildiğinde çalışacak yan etkileri yönetir.
  • useContext(): Uygulama genelinde durum paylaşımını kolaylaştırır.
  • useReducer(): Daha karmaşık durum yönetimi senaryoları için kullanılır.
  • useRef(): DOM elementlerine veya bir bileşenin önceki durumuna doğrudan erişim sağlar.

React projelerinde fonksiyonel bileşenler oluştururken en sık kullanılan araçlardan biri State Hook‘tur. State Hook, bir bileşenin durumunu kolayca yönetmek ve durumu güncellediğinizde bileşeni otomatik olarak yeniden render etmek için ideal bir çözümdür.

State Hook Kullanımı

State Hook, React kütüphanesinden import edilerek gelir. Kullanmak için useState‘i içeri aktararak başlarsınız:

import React, { useState } from 'react';

useState() çağrıldığında bir dizi döner. Bu dizide iki değer bulunur:

  1. Güncel durum değeri: Durumun mevcut değerini içerir.
  2. Durum güncelleyici fonksiyon: Durum değerini değiştirmek için kullanılır.

Bu iki değeri kullanarak, bir verinin mevcut durumunu takip edebilir ve gerektiğinde değiştirebilirsiniz. Dizi yapısını çözümlemek için aşağıdaki gibi bir “destructuring” işlemi yapılır:

const [durum, durumGuncelle] = useState(başlangıçDeğeri);

Örnek üzerinden inceleyelim:

import React, { useState } from 'react';

function Anahtar() {
  const [durum, setDurum] = useState('Kapalı');

  return (
    <div>
      <p>Anahtar şu anda: {durum}</p>
      <button onClick={() => setDurum('Açık')}>Aç</button>
      <button onClick={() => setDurum('Kapalı')}>Kapat</button>
    </div>
  );
}

export default Anahtar;

Butonlara tıklanınca, setDurum fonksiyonu çağrılarak durum güncellenir. Eğer:

  • setDurum('Açık') çağrılmışsa durum ‘Açık’ olarak değiştirilir.
  • React, bu değişikliği algılayarak bileşeni yeniden render eder ve güncel durumu ekrana yansıtır.

useState, durumun renderlar arasında korunmasını sağlar. Bir bileşen her yeniden render edildiğinde, React useState‘in önceki durum değerini hatırlar ve güncel durumu kullanır. Böylece, her render döngüsünde bileşenin en güncel halini göstermek kolaylaşır. Bir başka örnek üzerinden inceleyelim:

import React, { useState } from 'react';

function TemaDegistirici() {
  const [tema, setTema] = useState('Aydinlik');

  return (
    <div style={{ backgroundColor: tema === 'Aydınlık' ? '#fff' : '#333', color: tema === 'Aydınlık' ? '#000' : '#fff', padding: '20px' }}>
      <p>Seçili tema: {tema}</p>
      <button onClick={() => setTema('Aydinlik')}>Aydınlık Tema</button>
      <button onClick={() => setTema('Karanlik')}>Karanlık Tema</button>
    </div>
  );
}

export default TemaDegistirici;

Bu kodumuzda, tema değiştirildiğinde arka plan ve yazı rengi dinamik olarak güncellenir. Eğer setTema ile durum değiştirilirse React bunu algılar ve bileşeni yeniden render ederek güncel temayı uygular.

State Hook, React’in modern fonksiyonel bileşenlerinde durum yönetimini sadeleştirir. Durum güncellemelerinin ardından bileşeni yeniden render etmek için karmaşık işlemlere gerek kalmadan, yalnızca useState kullanarak uygulamalarınızı daha etkileşimli ve dinamik hale getirebilirsiniz. Bir başka örnekle inceleyelim:

ColorPicker.js

State Başlatma: React’te Durumun İlk Değerini Belirlemek

React’in State Hook‘u, yalnızca string gibi basit veri türlerini değil, aynı zamanda boolean, sayılar, diziler ve nesneler gibi farklı veri türlerini de yönetebilir. Bu esneklik, fonksiyonel bileşenlerde dinamik durum yönetimini oldukça güçlü bir hale getirir. Durumun ilk değerini ayarlamak ise uygulamanın başlangıç durumunu netleştirmek için kritik bir adımdır.

import React, { useState } from 'react';

function YukleniyorDurumu() {
  const [yukleniyorMu, setYukleniyorMu] = useState();

  return (
    <div>
      <p>Veri durumu: {yukleniyorMu ? 'Yükleniyor' : 'Yüklenmiyor'}</p>
      <button onClick={() => setYukleniyorMu(true)}>Yükleniyor Olarak Ayarla</button>
      <button onClick={() => setYukleniyorMu(false)}>Yüklenmiyor Olarak Ayarla</button>
    </div>
  );
}

export default YukleniyorDurumu;

Bu bileşende, yukleniyorMu bir boolean değeri tutuyor. Butonlara tıklayarak setYukleniyorMu ile durumu güncelleyebilirsiniz. Ancak, başlangıçta yukleniyorMu tanımlanmamış (undefined) durumdadır.

State’i başlatmak için useState fonksiyonuna bir başlangıç değeri argüman olarak verilir. Örneğin, bileşen yüklenirken yukleniyorMu değerini varsayılan olarak true yapmak istiyorsak şöyle yapabiliriz:

const [yukleniyorMu, setYukleniyorMu] = useState(true);

Bu güncelleme, bileşeninizin başlangıçtaki davranışını açık bir şekilde tanımlar ve şu avantajları sağlar:

  1. İlk Render için Değer Belirleme: İlk render sırasında, useState fonksiyonuna verilen başlangıç değeri kullanılır.
  2. Sonraki Güncellemeler: setYukleniyorMu çağrıldığında, başlangıç değeri yerine güncellenen yeni değer kullanılır.
  3. Yeniden Render Durumunda Değerin Korunması: Bileşen yeniden render edildiğinde, React önceki render’ın durum değerini hatırlar ve aynı değeri kullanmaya devam eder.

Durum başlatmanın ne kadar faydalı olduğunu anlamak için bir başka senaryo ele alalım. Bir sayaç uygulamasında başlangıç değerini sıfır olarak belirleyebiliriz:

import React, { useState } from 'react';

function Sayac() {
  const [sayi, setSayi] = useState(0);

  return (
    <div>
      <p>Sayaç: {sayi}</p>
      <button onClick={() => setSayi(sayi + 1)}>Artır</button>
      <button onClick={() => setSayi(sayi - 1)}>Azalt</button>
    </div>
  );
}

export default Sayac;

Bu bileşende, useState(0) başlangıç değeri olarak sıfırı belirler. İlk render sırasında sayaç sıfırdan başlar ve butonlara tıklanarak artırılıp azaltılabilir.

JSX Dışında State Güncelleme: Giriş Alanları için React State Yönetimi

React projelerinde bir kullanıcının metin girdiği alanları yönetmek için State Hook sıkça kullanılır. Giriş alanına yazılan değerlerin güncel durumunu kontrol etmek ve durumu güncellemek için bir yaklaşım geliştirilebilir. Örneğin:

import React, { useState } from 'react';

export default function EmailGirisAlani() {
  const [email, setEmail] = useState('');
  const handleChange = ({ target }) => setEmail(target.value);

  return (
    <input value={email} onChange={handleChange} />
  );
}
  1. State Tanımlama:
    useState('') kullanılarak email adında bir durum ve setEmail adında bir durum güncelleyici fonksiyon oluşturulur. Başlangıç değeri boş bir string olarak ayarlanmıştır.
  2. Event Handler Tanımlama:
    handleChange fonksiyonu, kullanıcı metin kutusuna her bir harf yazdığında çalışır. Bu fonksiyon, yazılan değeri (event.target.value) alır ve setEmail ile günceller.
  3. Event Listener Ekleme:
    JSX içindeki <input> etiketine onChange özelliği eklenmiştir. Bu özellik, kullanıcının bir şey yazdığı her an handleChange fonksiyonunu tetikler.

Event handler’ları JSX’in dışında tanımlamak, karmaşık durum yönetimlerini daha düzenli ve okunabilir hale getirir. Örneğin, aşağıdaki iki yöntem aynı işlevi görür:

1. İçerde Tanımlama

<input value={email} onChange={(event) => setEmail(event.target.value)} />

2. Dışarda Tanımlama

const handleChange = (event) => setEmail(event.target.value);
<input value={email} onChange={handleChange} />

Event handler’ı JSX dışında tanımlamak, özellikle daha karmaşık işlemler gerektiğinde avantaj sağlar. Örneğin:

const handleChange = ({ target }) => {
  if (target.value.includes('@')) {
    setEmail(target.value);
  } else {
    console.log('Geçersiz e-posta formatı');
  }
};

Event handler’ı basitleştirmek için farklı yöntemler kullanılabilir:

1. Temel Yapı:

const handleChange = (event) => {
  const yeniEmail = event.target.value;
  setEmail(yeniEmail);
};

2. Tek Satırlık Yapı:

const handleChange = (event) => setEmail(event.target.value);

3. Destructuring Kullanımı:

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

Üç yapı da aynı şekilde çalışır. Kodun netliği ve okunabilirliği açısından en sade yapıyı kullanmak tercihiniz olsun. Şimdi daha kapsamlı bir örnek düşünelim. Kullanıcının e-posta adresi geçerli mi kontrol eden bir bileşen kodlayalım:

import React, { useState } from 'react';

export default function EmailKontrol() {
  const [email, setEmail] = useState('');
  const [hata, setHata] = useState('');

  const handleChange = ({ target }) => {
    const deger = target.value;
    setEmail(deger);
    setHata(deger.includes('@') ? '' : 'Geçersiz e-posta adresi');
  };

  return (
    <div>
      <input value={email} onChange={handleChange} placeholder="E-posta adresinizi girin" />
      {hata && <p style={{ color: 'red' }}>{hata}</p>}
    </div>
  );
}

Bu örnek, kullanıcının yazdığı e-postanın geçerli olup olmadığını kontrol eder ve hata mesajını ekranda gösterir. Event handler içindeki mantık, bileşenin dinamik ve kullanıcı odaklı bir deneyim sunmasını sağlar.

React’te Durumu Önceki Değere Göre Güncellemek

React’te bir bileşenin durumunu (state) güncellemek için çoğu zaman yeni bir değer belirlemek yeterlidir:

setState(yeniDurumDeğeri);

React bileşenlerinde “durum” (state) güncellemeleri asenkron bir şekilde gerçekleşir. Bu ise bazı durumlarda güncelleme tamamlanmadan önce başka işlemlerin devreye girmesine neden olabilir. Böyle bir senaryoda, durum değerinin eski kalma riski ortaya çıkar. Eğer yeni durum, mevcut duruma bağlıysa daha güvenilir bir yöntem olan “geri çağırma” (callback) fonksiyonu kullanılır. Bu yöntem, doğru değerlerle güncelleme yapılmasını sağlar.

Aşağıdaki örnekte, bir sayaç uygulaması yer alıyor. Her butona tıklama, sayaç değerini bir artırıyor.

import React, { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);

  const increment = () => setCount((prevCount) => prevCount + 1);

  return (
    <div>
      <p>You've clicked the button: {count} times</p>
      <button onClick={increment}>Click here!</button>
    </div>
  );
}
  1. Başlangıç Değeri Tanımlama:
    useState(0) çağrılarak count adlı bir durum ve setCount adlı bir durum güncelleyici fonksiyon oluşturulur. Sayacın başlangıç değeri 0 olarak ayarlanır.
  2. Event Handler Kullanımı:
    increment fonksiyonu, setCount içinde bir geri çağırma fonksiyonu alır. Bu geri çağırma, önceki durum değerini (prevCount) argüman olarak alır ve yeni durumu hesaplar.
  3. Geri Çağırma ile Güvenilir Güncelleme:
    Her bir tıklamada React, önceki durumu alır ve yeni değeri doğru şekilde günceller. Bu yöntem, hızlı ardışık tıklamalar gibi durumlarda bile doğru sonuçlar elde edilmesini sağlar.

React’te durum güncellemeleri asenkron çalışır, yani aynı anda birden fazla durum güncellemesi yapılabilir. Bu gibi durumlarda, doğrudan setCount(count + 1) kullanmak yanlış sonuçlara yol açabilir. Örneğin:

setCount(count + 1);
setCount(count + 1);

Burada iki kez artırma yapmak istiyorsunuz ama durum hala eski değer üzerinden işlem görür ve sadece bir kez artırılmış gibi çalışır.

Geri çağırma fonksiyonu kullanırsanız:

setCount((prevCount) => prevCount + 1);
setCount((prevCount) => prevCount + 1);

React, her işlemi sırayla yapar ve mevcut durumu doğru bir şekilde artırır. Sonuçta sayaç değeri iki kez artırılır.

Diyelim ki sayaç şu anda 5.

  • İlk tıklamada React prevCount olarak 5 değerini alır ve prevCount + 1 işlemini yapar, yeni değer 6 olur.
  • İkinci tıklamada prevCount artık 6 olur ve bir artırılır, yeni değer 7 olur.

Durum değerini önceki değere göre güncellemek gerektiğinde, geri çağırma fonksiyonu kullanmak en güvenli yöntemdir. Asenkron güncellemelerin neden olabileceği yanlış durum değerlerini önler ve uygulamanın performansını korur. Daha güvenilir ve doğru sonuçlar elde etmek için bu yöntem kullanılmalıdır.

QuizNavBar.js

React’te Dizi Kullanımı: Durum Yönetimi ve JSX Listeleri

JavaScript dizileri, React uygulamalarında JSX listelerini yönetmek ve render etmek için harika bir veri modelidir. React ile bir hamburger siparişi veren için bir örnek üzerinde duralım:

import React, { useState } from 'react';

// Hamburger için mevcut malzeme seçenekleri
const ingredients = ['Domates', 'Marul', 'Peynir', 'Soğan'];

export default function CustomBurger() {
  const [selectedIngredients, setSelectedIngredients] = useState([]);

  const toggleIngredient = ({ target }) => {
    const clickedIngredient = target.value;
    setSelectedIngredients((prev) => {
      if (prev.includes(clickedIngredient)) {
        // Seçilen malzemeyi kaldırır
        return prev.filter(ingredient => ingredient !== clickedIngredient);
      } else {
        // Seçilen malzemeyi ekler
        return [clickedIngredient, ...prev];
      }
    });
  };

  return (
    <div>
      {ingredients.map(ingredient => (
        <button value={ingredient} onClick={toggleIngredient} key={ingredient}>
          {selectedIngredients.includes(ingredient) ? 'Çıkar ' : 'Ekle '}
          {ingredient}
        </button>
      ))}
      <p>Hazırlanan hamburger: {selectedIngredients.join(', ')}</p>
    </div>
  );
}

Yukarıdaki kodumuzda iki farklı türde dizi kullanılmıştır:

  1. Statik Dizi: ingredients dizisi sabit malzemeleri içerir ve değişmez. Statik veriler, işlev bileşenlerinin dışında tanımlanır. Bu, her yeniden render işleminde bu verilerin tekrar oluşturulmamasını sağlar.
  2. Dinamik Dizi: selectedIngredients dizisi, kullanıcının seçtiği malzemeleri tutar. Bu dizi, kullanıcı etkileşimlerine göre değişir. Başlangıçta boş bir dizi olarak tanımlanır ve kullanıcı bir butona tıkladığında güncellenir.

Dizi İşlemleriyle Durum Yönetimi

Malzeme seçiminde toggleIngredient fonksiyonu tetiklenir. Bu fonksiyon, tıklanan malzemenin zaten seçili olup olmadığını kontrol eder.

  • Eğer malzeme seçiliyse, filter() metodu ile bu malzeme diziden kaldırılır.
  • Eğer seçili değilse, ...prev spread operatörü ile önceki dizi kopyalanır ve yeni malzeme eklenir.

Dizi güncellenirken eski diziyi doğrudan değiştirmek yerine yeni bir dizi oluşturulur. Bu yaklaşım, React’in durum değişikliklerini daha etkili bir şekilde takip etmesini sağlar.

Kullanılan Dizi Metotları

  • includes(): Bir elemanın dizide olup olmadığını kontrol eder.
  • filter(): Şartı sağlayan elemanları yeni bir diziye ekler.
  • map(): Dizideki her eleman için bir işlem uygular ve yeni bir çıktı üretir.

Aşağıdaki örneği inceleyiniz:

BookWishlist.js
BookList.js
bookData.js

React’te Nesnelerle Durum Yönetimi

React uygulamalarında, birbiriyle ilişkili değişkenleri yönetmek için nesnelerle durum (state) kullanmak oldukça etkili bir yöntemdir. Bunun nasıl çalıştığını anlamak için bir kayıt formu örneği inceleyelim:

import React, { useState } from "react";

export default function EditProfile() {
  const [profile, setProfile] = useState({});

  const handleChange = ({ target }) => {
    const { name, value } = target;
    setProfile((prevProfile) => ({
      ...prevProfile,
      [name]: value
    }));
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    alert(JSON.stringify(profile, '', 2));
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={profile.firstName || ''}
        onChange={handleChange}
        name="firstName"
        type="text"
        placeholder="First Name"
      />
      <input
        value={profile.lastName || ''}
        onChange={handleChange}
        type="text"
        name="lastName"
        placeholder="Last Name"
      />
      <input
        value={profile.bday || ''}
        onChange={handleChange}
        type="date"
        name="bday"
      />
      <input
        value={profile.password || ''}
        onChange={handleChange}
        type="password"
        name="password"
        placeholder="Password"
      />
      <button type="submit">Save Profile</button>
    </form>
  );
}

Kodumuzu maddeler halinde inceleyelim:

1. Durum Tanımlama

const [profile, setProfile] = useState({});
  • profile: Kullanıcının profil bilgilerini tutar. Başlangıçta boş bir nesnedir.
  • setProfile: Profili güncellemek için kullanılan fonksiyondur.

2. handleChange Fonksiyonu

const handleChange = ({ target }) => {
  const { name, value } = target;
  setProfile((prevProfile) => ({
    ...prevProfile,
    [name]: value
  }));
};

Bu fonksiyon, formdaki herhangi bir girdiye yazıldığında tetiklenir:

  • target: Değişen girdi elemanını ifade eder.
  • name: Hangi girdinin değiştiğini belirtir (örneğin, firstName, lastName).
  • value: Girdi elemanına yazılan yeni değeri alır.

setProfile:

  • Önceki profil nesnesini kopyalar (...prevProfile).
  • Değişen girdinin name değerine karşılık gelen anahtarı günceller.

4. handleSubmit Fonksiyonu

const handleSubmit = (event) => {
  event.preventDefault();
  alert(JSON.stringify(profile, '', 2));
};
  • Formun gönderilmesini engeller (event.preventDefault()), böylece sayfa yeniden yüklenmez.
  • Profil nesnesini JSON formatında bir uyarı kutusunda gösterir.

5. Kaydet Düğmesi

<button type="submit">Save Profile</button>
  • Form gönderildiğinde handleSubmit fonksiyonu tetiklenir.
  • Profil bilgileri uyarı kutusunda gösterilir.

Örnek bir kullanıcı akışı çizelim:

  1. Kullanıcı “John”, “Doe”, “1990-01-01”, “password123” değerlerini girsin.
  2. profile durumu şu şekilde güncellenir:
{
  firstName: "John",
  lastName: "Doe",
  bday: "1990-01-01",
  password: "password123"
}
  1. Kaydet düğmesine basıldığında uyarı kutusunda profil şu şekilde görünür:
{
  "firstName": "John",
  "lastName": "Doe",
  "bday": "1990-01-01",
  "password": "password123"
}

Nesnelerle durum yönetimi, birden fazla ilişkili veriyi kolayca organize etmeye ve kontrol etmeye olanak tanır. Spread operatörü ve dinamik anahtarlar sayesinde, karmaşık formları bile minimal kodla yönetmek mümkün hale gelir. Bu yöntem, temiz ve yeniden kullanılabilir bir kod yazımı için idealdir.

React ile çalışırken, bazen bir dizi ya da nesne gibi veri koleksiyonlarını kullanmak faydalı olabilir. Ancak, birbirinden bağımsız olarak değişen veriler için ayrı durum değişkenleri oluşturmak genellikle işleri daha kolay hale getirir. Dinamik verileri yönetmek, veri modellerimizi olabildiğince basit tuttuğumuzda çok daha verimli olur.

Tek bir nesne kullanarak bir dersin durumunu tutmayı düşünelim. Örneğin:

function Ders() {
  const [durum, setDurum] = useState({
    mevcutNot: 'B',
    sinifArkadaslari: ['Ahmet', 'Zeynep', 'Eren'],
    dersDetaylari: {konu: 'Matematik', ogretmen: 'Sevda Hoca', sinif: 205},
    sinavlar: [{birim: 1, puan: 85}, {birim: 2, puan: 90}],
  });
}

Bu yöntem çalışır ancak bu nesnede herhangi bir şeyi güncellemek istediğimizde, tüm diğer değerleri de taşıyarak durumu yeniden oluşturmak karmaşık hale gelecektir. Örneğin, bir sınav notunu güncellemek için şöylesi bir kod yazabiliriz:

setDurum((onceki) => ({
  ...onceki,
  sinavlar: onceki.sinavlar.map((sinav) => {
    if (sinav.birim === guncellenenSinav.birim) {
      return {
        ...sinav,
        puan: guncellenenSinav.puan,
      };
    }
    return sinav;
  }),
}));

Bu tarz kodlar karmaşıklığı artırır ve hata yapma olasılığını yükseltir. Bunun yerine, birbirinden bağımsız değişen değerler için ayrı durum değişkenleri oluşturmak daha etkili bir çözüm sunar.

Aynı örneği yeniden düzenleyerek daha basit bir hale getirelim:

function Ders() {
  const [mevcutNot, setNot] = useState('B');
  const [sinifArkadaslari, setArkadaslar] = useState(['Ahmet', 'Zeynep', 'Eren']);
  const [dersDetaylari, setDetaylar] = useState({konu: 'Matematik', ogretmen: 'Sevda Hoca', sinif: 205});
  const [sinavlar, setSinavlar] = useState([{birim: 1, puan: 85}, {birim: 2, puan: 90}]);
}

Bu yaklaşım; kodun okunmasını, yazılmasını ve test edilmesini kolaylaştırır. Ayrıca, veriler kolayca yeniden kullanılabilir hale gelir. Ancak bu yaklaşım yerine aşağıdaki örnekte gibi yapalım:

Film.js

Çoğu zaman, bileşenler arasında veri aktarımı yaparken bu verileri bir koleksiyon içinde toplarız. Ancak, bileşen içinde birbirinden bağımsız olarak değişen veriler için ayrı durum değişkenleri kullanırız. Bu esneklik, React’in sunduğu hook’ların en önemli avantajlarından biridir. Doğru şekilde organize edilmiş verilerle, hem kodunuz daha sade olur hem de gelecekteki güncellemeler sırasında karışıklık yaşanmaz.

İlgili Makaleler

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Başa dön tuşu