State: Bir Bileşenin Hafızası

Bileşenler, etkileşimin bir sonucu olarak ekranda olanı değiştirmeye sıklıkla ihtiyaç duyarlar. Forma yazı yazmak, giriş alanını güncellemelidir, resim karuselinde “sonraki”ye tıklamak gösterilen resmi değiştirmelidir, “satın al” düğmesine tıklamak bir ürünü alışveriş sepetine koymalıdır. Bileşenler şeyleri “hatırlamalıdır”: mevcut girdi değeri, mevcut resim, alışveriş sepeti. React’te, bileşene özgü bu tür bellek state olarak adlandırılır.

Bunları öğreneceksiniz

  • useState Hook’u ile state değişkeni nasıl eklenir
  • useState Hook’unun döndürdüğü değer çifti
  • Birden fazla state değişkeni nasıl eklenir
  • Neden state’in yerel olarak adlandırıldığı

Normal bir değişken yeterli olmadığında

İşte bir heykel resmini oluşturan bir bileşen. “Sonraki” düğmesine tıklanarak index değişkeni 1, ardından 2 ve benzeri şekilde değiştirilerek bir sonraki heykel gösterilmelidir. Ancak bu çalışmayacaktır (deneyebilirsiniz!):

import { sculptureList } from './data.js';

export default function Gallery() {
  let index = 0;

  function handleClick() {
    index = index + 1;
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleClick}>
        Sonraki
      </button>
      <h2>
        <i>{sculpture.name}, </i>
        {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
      <p>
        {sculpture.description}
      </p>
    </>
  );
}

handleClick olay yöneticisi bir yerel değişken olan index’i güncelliyor. Ancak, iki şey bu değişikliğin görünür olmasını engelliyor:

  1. Yerel değişkenler renderlar arasında kalıcı değildir. React bu bileşeni ikinci kez renderladığında, herhangi bir yerel değişiklik göz önünde bulundurulmaz ve bileşen sıfırdan yeniden renderlanır.
  2. Yerel değişkenlere yapılan değişiklikler renderı tetiklemez. React, bileşeni yeni verilerle yeniden render etmesi gerektiğini fark etmez.

Yeni verilerle bir bileşeni güncellemek için, iki şeyin yapılması gerekir:

  1. Renderlar arasında verileri korumak.
  2. Bileşeni yeni verilerle yeniden render etmesi için React’i tetiklemek.

useState Hook’u bu iki şeyi sağlar:

  1. Renderlar arasında verileri saklamak için bir state değişkeni.
  2. Değişkeni güncellemek ve React’in bileşeni tekrar render etmesini tetiklemek için bir state setter fonksiyonu.

State değişkeni ekleme

Bir state değişkeni eklemek için, dosyanın üst kısmında React’ten useState öğesini içe aktarın:

import { useState } from 'react';

Ardından, bu satırı:

let index = 0;

bununla değiştirin

const [index, setIndex] = useState(0);

index bir state değişkeni ve setIndex ise bir setter fonksiyonudur.

Buradaki [ ve ] sözdizimine array destructuring denir ve bir diziden değerleri okumanızı sağlar. useState tarafından döndürülen dizi her zaman tam olarak iki öğeye sahiptir.

handleClick içinde birlikte bu şekilde çalışırlar:

function handleClick() {
setIndex(index + 1);
}

Artık “Sonraki” düğmesine tıklamak mevcut heykeli değiştirir:

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);

  function handleClick() {
    setIndex(index + 1);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleClick}>
        Sonraki
      </button>
       <h2>
        <i>{sculpture.name}, </i>
        {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
      <p>
        {sculpture.description}
      </p>
    </>
  );
}

İlk Hook’unuz ile tanışın

React’te, useState ve “use” ile başlayan diğer tüm fonksiyonlar Hook olarak adlandırılır.

Hook’lar yalnızca React rendering işlemi sırasında kullanılabilen özel fonksiyonlardır (bir sonraki sayfada daha ayrıntılı olarak ele alacağız). Bunlar farklı React özelliklerini “bağlamaya” izin verirler.

State bu özelliklerden sadece biri, ancak diğer Hook’larla daha sonra tanışacaksınız.

Tuzak

Hook’lar (use ile başlayan fonksiyonlar) yalnızca bileşenlerinizin en üst seviyesinde veya kendi Hook’larınızda çağrılabilir. Hook’ları koşullar, döngüler veya diğer iç içe geçmiş fonksiyonlar içinde çağıramazsınız. Hook’lar fonksiyonlar olsa da, bileşeninizin ihtiyaçları hakkında koşulsuz deklarasyonlar olarak düşünmek faydalıdır. React özelliklerini bileşeninizin başında “use” edersiniz, tıpkı dosyanızın başında modülleri “import” etmeniz gibi.

useState’in’ anatomisi

useState fonksiyonunu çağırdığınızda, React’e bu bileşenin bir şeyleri hatırlamasını istediğinizi söylüyorsunuz:

const [index, setIndex] = useState(0);

Bu örnekte, React’in indexi hatırlamasını istiyorsunuz.

Not

Bu çifti const [birOge, setBirOge] gibi adlandırmak geleneksel bir yöntemdir. İstediğiniz herhangi bir isim verebilirsiniz, ancak gelenekleşmiş yöntemler farklı projeler arasında şeylerin daha kolay anlaşılmasını sağlar.

useState için tek argüman state değişkeninizin başlangıç değeridir. Bu örnekte, indexin başlangıç değeri useState(0) ile 0 olarak ayarlanmıştır.

Bileşeniniz her render edildiğinde, useState size iki değer içeren bir dizi verir:

  1. Değerinizi saklayan state değişkeni (index).
  2. State değişkenini güncelleyebilen ve React’in bileşeni yeniden renderlaması için tetikleyen state setter fonksiyonu (setIndex).

İşte bunun işleyişi:

const [index, setIndex] = useState(0);
  1. Bileşeniniz ilk kez render edilir. useState’e index başlangıç değeri olarak 0 geçtiğinizden, [0, setIndex] olarak geri dönecektir. React, 0 değerinin en son state değeri olduğunu hatırlar.
  2. State’i güncellersiniz. Bir kullanıcı butona tıkladığında, setIndex(index + 1) çağırır. index değeri 0 olduğu için setIndex(1) çağrılır. Bu, React’e index’in artık 1 olduğunu hatırlamasını söyler ve başka bir render işlemini tetikler.
  3. Bileşeninizin ikinci render edilişi. React hala useState(0)’ı görür, ancak React index’in artık 1 olarak ayarlandığını hatırladığıdan, [1, setIndex] olarak geri döner.
  4. Ve böyle devam eder!

Bir bileşene birden fazla state değişkeni verme

Bir bileşende istediğiniz kadar çok sayıda farklı tipte state değişkeni olabilir. Bu bileşende, bir index sayısı ve “Detayları göster”e tıkladığınızda değiştirilen bir boolean showMore değeri:

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);

  function handleNextClick() {
    setIndex(index + 1);
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Sonraki
      </button>
       <h2>
        <i>{sculpture.name}, </i>
        {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        Detayları {showMore ? 'Gizle' : 'Göster'}
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
    </>
  );
}

Bu örnekte olduğu gibi index ve showMore gibi state’ler birbirleriyle ilişkisiz olduğunda, birden fazla state değişkenine sahip olmak iyi bir fikirdir. Ancak iki state değişkenini sık sık birlikte değiştirdiğinizi fark ederseniz, bunları tek bir değişkende birleştirmek daha pratik olabilir. Örneğin, çok sayıda alana sahip bir formunuz varsa, alan başına state değişkeni yerine bir nesneyi tutan tek bir state değişkenine sahip olmak daha uygundur. Daha fazla ipucu için Choosing the State Structure bölümünü okuyun.

Derinlemesine İnceleme

React hangi state’in geri döneceğini nasıl bilir?

useState çağrısının hangi state değişkenine referans verdiği hakkında herhangi bir bilgi almadığını fark etmiş olabilirsiniz. useState’e geçilen bir “tanımlayıcı” yoktur, peki hangi state değişkenini döndüreceğini nasıl bilir? Fonksiyonlarınızı ayrıştırmak gibi bir sihre mi güveniyor? Cevap hayır.

Bunun yerine, kısa sözdizimlerini etkinleştirmek için Hook’lar aynı bileşenin her render edilişinde sabit bir çağrı sırasına dayanır. Bu pratikte iyi çalışır, çünkü yukarıdaki kuralı izlerseniz (“Hook’ları yalnızca en üst düzeyde çağırın”), Hook’lar her zaman aynı sırada çağrılacaktır. Ek olarak bir linter eklentisi çoğu hatayı yakalar.

Dahili olarak, React her bileşen için bir state çifti dizisi tutar. Ayrıca, render edilmeden önce 0 olarak ayarlanan mevcut çift indeksini de tutar. Her useState’i çağırdığınızda, React size bir sonraki state çiftini verir ve indeksi artırır. Bu mekanizma hakkında daha fazla bilgi için React Hooks: Not Magic, Just Arrays yazısını okuyabilirsiniz.

Bu örnekte React kullanılmıyor ancak useStatein dahili olarak nasıl çalıştığı hakkında bir fikir verir:

let componentHooks = [];
let currentHookIndex = 0;

// useState'nın React içinde nasıl çalıştığı (basitleştirilmiş).
function useState(initialState) {
  let pair = componentHooks[currentHookIndex];
  if (pair) {
    // Bu ilk render değil,
    // dolayısıyla state çifti zaten var.
    // Onu döndür ve bir sonraki Hook çağrısı için hazırla.
    currentHookIndex++;
    return pair;
  }

  // Bu ilk render,
  // state çifti oluştur ve depola.
  pair = [initialState, setState];

  function setState(nextState) {
    // Kullanıcı state değişikliği talep ettiğinde,
    // yeni değeri çiftin içine yerleştir.
    pair[0] = nextState;
    updateDOM();
  }

  // Gelecekteki renderlar için çifti depola
  // ve bir sonraki Hook çağrısı için hazırla.
  componentHooks[currentHookIndex] = pair;
  currentHookIndex++;
  return pair;
}

function Gallery() {
  // Her useState() çağrısı bir sonraki çifti alacaktır.
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);

  function handleNextClick() {
    setIndex(index + 1);
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  // Bu örnek React kullanmadığından
  // JSX yerine bir çıktı nesnesi geri döndürür.
  return {
    onNextClick: handleNextClick,
    onMoreClick: handleMoreClick,
    header: `${sculpture.name} by ${sculpture.artist}`,
    counter: `${index + 1} of ${sculptureList.length}`,
    more: `$Detayları {showMore ? 'Gizle' : 'Göster'}`,
    description: showMore ? sculpture.description : null,
    imageSrc: sculpture.url,
    imageAlt: sculpture.alt
  };
}

function updateDOM() {
  // Bileşeni oluşturmadan önce
  // mevcut Hook dizinini sıfırla.
  currentHookIndex = 0;
  let output = Gallery();

  // Çıktıya uygun olarak DOM'u güncelle.
  // Bu kısım React tarafından sizin için yapılır.
  nextButton.onclick = output.onNextClick;
  header.textContent = output.header;
  moreButton.onclick = output.onMoreClick;
  moreButton.textContent = output.more;
  image.src = output.imageSrc;
  image.alt = output.imageAlt;
  if (output.description !== null) {
    description.textContent = output.description;
    description.style.display = '';
  } else {
    description.style.display = 'none';
  }
}

let nextButton = document.getElementById('nextButton');
let header = document.getElementById('header');
let moreButton = document.getElementById('moreButton');
let description = document.getElementById('description');
let image = document.getElementById('image');
let sculptureList = [{
  name: 'Homenaje a la Neurocirugía',
  artist: 'Marta Colvin Andrade',
  description: "Colvin'in öncelikle pre-Hispanik sembollere gönderme yapan soyut temalarıyla tanınmasına rağmen, nöroşirurjiye bir saygı niteliğindeki bu devasa heykel, en tanınmış halka açık sanat eserlerinden biridir.",
  url: 'https://i.imgur.com/Mx7dA2Y.jpg',
  alt: 'Parmak uçlarında insan beynini nazikçe tutan iki çapraz ellerden oluşan bronz bir heykel.'
}, {
  name: 'Floralis Genérica',
  artist: 'Eduardo Catalano',
  description: "Buenos Aires'te bulunan bu devasa (75 ft. veya 23 m) gümüş çiçek, akşamları veya güçlü rüzgarlar estiğinde yapraklarını kapatarak ve sabahları açarak hareket etmek üzere tasarlanmıştır.",
  url: 'https://i.imgur.com/ZF6s192m.jpg',
  alt: 'Ayna gibi yansıtıcı yaprakları ve güçlü erkek organları olan devasa bir metal çiçek heykeli.'
}, {
  name: 'Eternal Presence',
  artist: 'John Woodrow Wilson',
  description: 'Wilson, eşitlik, sosyal adalet ve insanlığın temel ve ruhsal nitelikleriyle ilgilenmesiyle tanınıyordu. Bu devasa (7 fit veya 2,13 m) bronz, "evrensel insanlık duygusu ile dolu sembolik bir Siyah varlığı" olarak nitelendirdiği şeyi temsil eder.',
  url: 'https://i.imgur.com/aTtVpES.jpg',
  alt: 'İnsan kafasını tasvir eden heykel her zaman varmış gibi görünüyor ve hüzünlü. Sakinlik ve huzur yayar.'
}, {
  name: 'Moai',
  artist: 'Bilinmeyen Sanatçı',
  description: "Paskalya Adası'nda bulunan, erken Rapa Nui halkı tarafından yaratılmış 1.000 moai veya devasa anıtsal heykelden oluşan bir koleksiyondur ve bazıları tanrılaştırılmış ataları temsil ettiğine inanıyor.",
  url: 'https://i.imgur.com/RCwLEoQm.jpg',
  alt: 'Somut ifadeleriyle orantısız büyük başlara sahip üç devasa taş büst.'
}, {
  name: 'Blue Nana',
  artist: 'Niki de Saint Phalle',
  description: 'Nanalar muzaffer yaratıklar, kadınlık ve annelik sembolleridir. Saint Phalle, Nanalar için başlangıçta kumaş ve buluntu nesneler kullanmış, daha sonra daha canlı bir etki elde etmek için polyester kullanmıştır.',
  url: 'https://i.imgur.com/Sd1AgUOm.jpg',
  alt: 'Neşe saçan renkli kostümüyle dans eden tuhaf bir kadın figürünün büyük bir mozaik heykeli.'
}, {
  name: 'Ultimate Form',
  artist: 'Barbara Hepworth',
  description: "Bu soyut bronz heykel, Yorkshire Heykel Parkı'nda bulunan The Family of Man serisinin bir parçasıdır. Hepworth, dünyanın birebir temsillerini yaratmak yerine, insanlardan ve manzaralardan esinlenen soyut formlar geliştirmeyi tercih etmiştir.",
  url: 'https://i.imgur.com/2heNQDcm.jpg',
  alt: 'İnsan figürünü anımsatan, birbiri üzerine yığılmış üç unsurdan oluşan uzun bir heykel.'
}, {
  name: 'Cavaliere',
  artist: 'Lamidi Olonade Fakeye',
  description: "Dört kuşaktır ahşap oymacılığı yapan Fakeye'nin eserleri geleneksel ve çağdaş Yoruba temalarını harmanlıyor.",
  url: 'https://i.imgur.com/wIdGuZwm.png',
  alt: 'Desenlerle bezenmiş bir atın üzerinde odaklanmış bir yüze sahip bir savaşçının karmaşık bir ahşap heykeli.'
}, {
  name: 'Big Bellies',
  artist: 'Alina Szapocznikow',
  description: "Szapocznikow, gençliğin ve güzelliğin kırılganlığı ve geçiciliğine bir metafor olarak parçalanmış bedenlerin heykelleriyle tanınır. Bu heykel, birbirine yığılmış iki çok gerçekçi büyük karın kasını tasvir eder, her biri yaklaşık beş ayak (1,5m) yüksekliğindedir.",
  url: 'https://i.imgur.com/AlHTAdDm.jpg',
  alt: 'Heykel, klasik heykellerdeki göbeklerden oldukça farklı olan kıvrımlardan oluşan bir çağlayanı andırıyor.'
}, {
  name: 'Terracotta Army',
  artist: 'Bilinmeyen Sanatçı',
  description: "Terracotta Ordusu, Çin'in ilk İmparatoru Qin Shi Huang'ın ordularını tasvir eden bir terracotta heykel koleksiyonudur. Ordu 8.000'den fazla asker, 520 atlı 130 savaş arabası ve 150 süvari atından oluşuyordu.",
  url: 'https://i.imgur.com/HMFmH6m.jpg',
  alt: 'Her biri benzersiz bir yüz ifadesine ve zırha sahip, vakur savaşçıların 12 pişmiş toprak heykeli.'
}, {
  name: 'Lunar Landscape',
  artist: 'Louise Nevelson',
  description: "Nevelson, New York'un enkazından topladığı ve daha sonra anıtsal yapılarda bir araya getireceği nesnelerle tanınıyordu. Bu eserinde yatak direği, hokkabaz iğnesi ve koltuk parçası gibi birbirinden farklı parçaları kullanmış, bunları çivileyip yapıştırarak Kübizm'in geometrik mekân ve biçim soyutlamasının etkisini yansıtan kutular haline getirmiştir.",
  url: 'https://i.imgur.com/rN7hY6om.jpg',
  alt: 'Tek tek unsurların başlangıçta ayırt edilemediği siyah mat bir heykel.'
}, {
  name: 'Aureole',
  artist: 'Ranjani Shettar',
  description: 'Shettar geleneksel ve modern olanı, doğal ve endüstriyel olanı birleştiriyor. Sanatı insan ve doğa arasındaki ilişkiye odaklanıyor. Çalışmaları hem soyut hem de figüratif olarak zorlayıcı, yerçekimine meydan okuyan ve "beklenmedik malzemelerin iyi bir sentezi" olarak tanımlanmıştır.',
  url: 'https://i.imgur.com/okTpbHhm.jpg',
  alt: 'Beton duvara monte edilmiş ve yere inen soluk tel benzeri bir heykel. Hafif görünüyor.'
}, {
  name: 'Hippos',
  artist: 'Taipei Zoo',
  description: 'Taipei Hayvanat Bahçesi, oyun oynayan su aygırlarının yer aldığı bir Su Aygırı Meydanı yaptırdı.',
  url: 'https://i.imgur.com/6o5Vuyu.jpg',
  alt: 'Bir grup bronz su aygırı heykeli sanki yüzüyormuş gibi kaldırımdan çıkıyor.'
}];

// Kullanıcı arayüzünü ilk state ile eşleştir.
updateDOM();

React’i kullanmak için bunu anlamak zorunda değilsiniz, ancak bu sizin için yararlı bir zihinsel model olabilir.

State izole edilmiştir ve özeldir

State, ekrandaki bir bileşen örneği için yereldir. Başka bir deyişle, aynı bileşeni iki kez render ederseniz, her bir kopya tamamen izole edilmiş state’e sahip olacaktır Birini değiştirmek diğerini etkilemeyecektir.

Bu örnekte, önceki örnekteki Gallery bileşeni çalışma mantığı değiştirilmeden iki kez render edilir. Galerilerin her birinin içindeki düğmelere tıklamayı deneyin. State’lerinin bağımsız olduğuna dikkat edin:

import Gallery from './Gallery.js';

export default function Page() {
  return (
    <div className="Page">
      <Gallery />
      <Gallery />
    </div>
  );
}

Bu, state’i modülünüzün en üstünde bildirebileceğiniz normal değişkenlerden farklı kılan şeydir. State belirli bir fonksiyon çağrısına veya koddaki bir yere bağlı değildir, ancak ekrandaki belirli bir yere “yereldir”. İki <Gallery /> bileşeni oluşturdunuz, bu nedenle state’leri ayrı ayrı saklanır.

Ayrıca Page bileşeninin Gallery state’i hakkında hiçbir şey “bilmediğine” ve hatta herhangi bir state’e sahip olup olmadığına dikkat edin. Prop’ların aksine, state, onu bildiren bileşene tamamen özeldir. Üst bileşen onu değiştiremez. Bu, bileşenlerin geri kalanını etkilemeden herhangi bir bileşene state eklemenize veya kaldırmanıza olanak tanır.

Peki ya her iki galerinin de state’lerinin senkronize olmasını istiyorsanız? React’te bunu yapmanın doğru yolu, alt bileşenlerden state’i kaldırmak ve en yakın paylaşılan ebeveynlerine eklemektir. Sonraki birkaç sayfa tek bir bileşenin state’ini düzenlemeye odaklanacak, ancak bu konuya Sharing State Between Components bölümünde geri döneceğiz.

Özet

  • Bir bileşenin render işlemleri arasında bazı bilgileri “hatırlaması” gerektiğinde bir state değişkeni kullanın.
  • State değişkenleri useState Hook`u çağrılarak bildirilir.
  • Hook’lar use ile başlayan özel fonksiyonlardır. State gibi React özelliklerine “bağlanmanızı (hook into)” sağlarlar.
  • Hook’lar, size import’ları hatırlatabilir: koşulsuz olarak çağrılmalıdırlar. useState de dahil Hook’ları çağırmak, yalnızca bir bileşenin üst seviyesinde veya başka bir Hook’ta geçerlidir.
  • useState Hook’u bir çift değer döndürür: mevcut state ve onu güncelleyecek fonksiyon.
  • Birden fazla state değişkeniniz olabilir. Dahili olarak, React bunları sıralarına göre eşleştirir.
  • State bileşene özeldir. Eğer iki yerde render ederseniz, her kopya kendi state’ini oluşturur.

Son heykelde “Sonraki” tuşuna bastığınızda kod çöküyor. Çökmeyi önlemek için mantığı düzeltin. Bunu olay yöneticisine ekstra mantık ekleyerek veya işlem mümkün olmadığında düğmeyi devre dışı bırakarak yapabilirsiniz.

Çökmeyi düzelttikten sonra, bir önceki heykeli gösteren “Önceki” düğmesi ekleyin. İlk heykelde çökmemesi gerekir.

import { useState } from 'react';
import { sculptureList } from './data.js';

export default function Gallery() {
  const [index, setIndex] = useState(0);
  const [showMore, setShowMore] = useState(false);

  function handleNextClick() {
    setIndex(index + 1);
  }

  function handleMoreClick() {
    setShowMore(!showMore);
  }

  let sculpture = sculptureList[index];
  return (
    <>
      <button onClick={handleNextClick}>
        Sonraki
      </button>
       <h2>
        <i>{sculpture.name}, </i>
        {sculpture.artist}
      </h2>
      <h3>  
        ({index + 1} of {sculptureList.length})
      </h3>
      <button onClick={handleMoreClick}>
        Detayları {showMore ? 'Gizle' : 'Göster'}
      </button>
      {showMore && <p>{sculpture.description}</p>}
      <img 
        src={sculpture.url} 
        alt={sculpture.alt}
      />
    </>
  );
}