Bazı JavaScript fonksiyonları saf olarak adlandırılır. Saf fonksiyonlar sadece bir hesaplama yaparlar ve başka bir işlem gerçekleştirmezler. Bileşenlerinizi sadece saf fonksiyonlar olarak yazarak, kod tabanınız büyüdükçe ortaya çıkabilecek birçok karmaşık hatayı ve öngörülemeyen davranışları önleyebilirsiniz. Ancak, bu faydaları elde etmek için bazı kurallara uymalısınız.

Bunları öğreneceksiniz

  • Saflık nedir ve hatalardan kaçınmanıza nasıl yardımcı olur,
  • Değişiklikleri render aşaması dışında tutarak bileşenleri nasıl saf tutabileceğiniz,
  • Bileşenlerinizdeki hataları bulmak için Strict Modu’u nasıl kullanacağınız.

Saflık: Formüller olarak bileşenler

Bilgisayar biliminde (ve özellikle fonksiyonel programlama dünyasında), saf bir fonksiyon aşağıdaki özelliklere sahip fonksiyonlardır:

  • Kendi işine bakar. Çağrılmadan önce var olan herhangi bir nesneyi ve objeyi değiştirmez.
  • Aynı girdi, aynı çıktı. Aynı girdiler verildiğinde, saf bir fonksiyon her zaman aynı sonucu döndürmelidir.

Saf fonksiyonların bir örneğini zaten biliyor olabilirsiniz: matematikteki formüller.

Bu formülü ele alalım: y = 2x.

Eğer x = 2 ise y = 4’tür. Her zaman.

Eğer x = 3 ise y = 6’dır. Her zaman.

Eğer x = 3 ise, y günün zamanına veya borsanın durumuna bağlı olarak bazen 9 ya da –1 veya 2.5 olmaz.

Eğer y = 2x ve x = 3 ise, y her zaman 6’dır.

Eğer bunu bir JavaScript fonksiyonuna çevirseydik, şöyle görünürdü:

function double(number) {
return 2 * number;
}

Yukardaki örnekte, double saf bir fonksiyondur. Fonksiyona 3 parametresini geçerseniz, 6'yı döndürür. Her zaman.

React bu konseptin etrafında tasarlanmıştır. React yazdığınız her bileşenin saf bir fonksiyon olduğunu varsayar. Bu, yazdığınız React bileşenlerinin, aynı girdiler verildiğinde her zaman aynı JSX’i döndürmesi gerektiği anlamına gelir:

function Recipe({ drinkers }) {
  return (
    <ol>    
      <li>Boil {drinkers} cups of water.</li>
      <li>Add {drinkers} spoons of tea and {0.5 * drinkers} spoons of spice.</li>
      <li>Add {0.5 * drinkers} cups of milk to boil and sugar to taste.</li>
    </ol>
  );
}

export default function App() {
  return (
    <section>
      <h1>Spiced Chai Recipe</h1>
      <h2>For two</h2>
      <Recipe drinkers={2} />
      <h2>For a gathering</h2>
      <Recipe drinkers={4} />
    </section>
  );
}

Drinkers parametresine {2} değerini verip, Recipe'ye geçerseniz, 2 bardak su içeren JSX’i döndürür. Her zaman.

Drinkers parametresine {4} değerini verip, 4 bardak su içeren JSX’i döndürür. Her zaman.

Tıpkı bir matematik formülü gibi.

Bileşenlerinizi de bir tarif gibi düşünebilirsiniz: bunları takip eder ve pişirme esnasında yeni malzemeler eklemezseniz, her zaman aynı yemeği yaparsınız. Bu “yemek”, bileşenin React’e render için sağladığı JSX’tir.

A tea recipe for x people: take x cups of water, add x spoons of tea and 0.5x spoons of spices, and 0.5x cups of milk

Rachel Lee Nabors tarafından görselleştirilmiştir.

Yan Etkileri: isten(mey)en sonuçlar

React’in render işlemi her zaman saf olmalıdır. Bileşenler yalnızca JSX’lerini döndürmeli, ve render işleminden önce var olan herhangi bir nesne veya değişkeni değiştirmemelidir - aksi takdirde bileşenler saf olmaktan çıkar!

İşte bu kuralı ihlal eden bir bileşen örneği:

let guest = 0;

function Cup() {
  // Bad: changing a preexisting variable!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}

Bu bileşen, kendisi dışında tanımlanmış bir misafir değişkenini okuyup yazıyor. Bu, bu bileşenin birden fazla kez çağrılması farklı JSX üreteceği anlamına gelir! Ve daha da fazlası, diğer bileşenler de misafir değişkenini okursa, ne zaman render edildiklerine bağlı olarak farklı JSX üreteceklerdir! Bu tahmin edilebilir değil.

y = 2x formülümüze geri dönersek, şimdi x = 2 olsa bile, y = 4’e güvenemeyiz. Testlerimiz başarısız olabilir, kullanıcılarımız şaşkına dönebilir, uçaklar düşebilir - nasıl karışık hatalara neden olacağını görebilirsiniz!

Bunun yerine, misafiri bir prop olarak geçerek bu bileşeni düzeltebilirsiniz:

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}

Artık bileşeniniz saf bir durumda, çünkü döndürdüğü JSX yalnızca misafir prop’una bağlı.

Genel olarak, bileşenlerinizin belirli bir sırada işlenmesiniz beklememelisiniz. y = 2x’i, y = 5x’ten önce veya sonra çağırmanız farketmez: Her iki formül de birbirinden bağımsız olarak çözülecektir. Aynı şekilde, her bileşen yalnızca “kendi için düşünmeli” ve render işlemi sırasında diğer bileşenlerle koordine etmeye veya onlara bağımlı olmaya çalışmamalıdır. Render işlemi bir okul sınavı gibi: her bileşen kendi JSX’ini hesaplamalıdır!

Derinlemesine İnceleme

StrictMode ile saf olmayan hesaplamaları algılama

Henüz hepsini kullanmamış olsanız da, React’te işleme sırasında okuyabileceğiniz üç tür girdi vardır: props, state, and context. Bu girişleri her zaman salt okunur olarak değerlendirmelisiniz.

Kullanıcı girişine yanıt olarak bir şeyi değiştirmek istediğinizde, bir değişkene yazmak yerine, state oluşturmalısınız. Bileşeniniz render edilirken önceden var olan değişkenleri veya nesneleri asla değiştirmemelisiniz.

React, geliştirme sırasında her bileşenin işlevini iki kez çağırdığı bir “Strict Mode” sunar. Strict Mode, bileşen işlevlerini iki kez çağırarak, bu kuralları çiğneyen bileşenlerin bulunmasına yardımcı olur.

Orijinal örneğin “Guest #2”, “Guest #4” ve “Guest #6” yerine “Guest #1”, “Guest #2” ve “Guest #3” yerine nasıl görüntülendiğine dikkat edin. Orijinal fonksiyon saf değildi, bu yüzden onu iki kez çağırmak onu bozdu. Ancak sabit saf fonksiyon, işlev her seferinde iki kez çağrılsa bile çalışır. Saf fonksiyonlar yalnızca hesaplama yapar, bu yüzden onları iki kez çağırmak hiçbir şeyi değiştirmez — tıpkı double(2)’yi iki kez çağırmanın döndürülen şeyi değiştirmemesi, y = 2x’i iki kez çözmenin y’yi değiştirmemesi gibi. Aynı girdiler, aynı çıktılar. Her zaman.

Strict Mode’un canlıda hiçbir etkisi yoktur, bu nedenle kullanıcılarınız için uygulamayı yavaşlatmaz. Strict Mode’u etkinleştirmek için kök bileşeninizi <React.StrictMode> içine sarabilirsiniz. Bazı kütüphaneler bunu varsayılan olarak yapar.

Yerel Mutasyon: Yerel bileşeninizin küçük sırrı

Yukarıdaki örnekteki sorun, bileşenin render edilirken önceden var olan bir değişkeni değiştirmesiydi. Bu genellikle biraz korkutucu görünmesi için mutasyon olarak adlandırılır. Saf fonksiyonlar, fonksiyonun kapsamı dışındaki değişkenleri veya çağrıdan önce oluşturulmuş nesneleri değiştirmez - bu onları saf olmayan fonksiyonlar yapar!

Ancak, render işlemi sırasında oluşturduğunuz değişkenleri ve nesneleri değiştirmek tamamen normaldir. Bu örnekte, [] bir dizi oluşturur, bunu bir cups değişkenine atar ve ardından içine bir düzine fincan eklersiniz:

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaGathering() {
  let cups = [];
  for (let i = 1; i <= 12; i++) {
    cups.push(<Cup key={i} guest={i} />);
  }
  return cups;
}

Eğer cups değişkeni veya [] dizisi TeaGathering fonksiyonunun dışında oluşturulmuş olsaydı, bu büyük bir sorun olurdu! Bu dizinin içine öğeler ekleyerek önceden var olan bir nesneyi değiştiriyor olacaktınız.

Ancak, TeaGathering içindeki aynı render işlemi sırasında oluşturduğunuz için bu tamamen normaldir. TeaGathering dışındaki hiçbir kod bunun olduğunu asla bilemeyecektir. Buna “yerel mutasyon” denir - bileşeninizin küçük bir sırrı gibi.

Yan etkilere neden olabileceğiniz yerler

Fonksiyonel programlama, büyük ölçüde saflığa dayanırken, bir noktada, bir yerde, bir şeyin değişmesi gerekir. Bu, programlamanın bir nevi amacıdır! Ekranın güncellenmesi, bir animasyonun başlatılması, verilerin değiştirilmesi gibi değişikliklere yan etkiler denir. Bunlar, render işlemi sırasında değil, “yan tarafta” meydana gelen şeylerdir.

React’te, yan etkiler genellikle olay yöneticileri içine yazılır. Olay Yöneticileri, bir işlem gerçekleştirdiğinizde (örneğin, bir düğmeye tıkladığınızda) React’ın çalıştırdığı fonksiyonlardır. Olay Yöneticileri bileşeninizin içinde tanımlanmış olsa da, bunlar işleme sırasında çalışmazlar! Bu nedenle olay işleyicilerinin saf olması gerekmez.

Diğer tüm seçenekleri tükettiyseniz ve yan etkiniz için doğru olay yöneticilieri’ni bulamıyorsanız, bileşeninizde biruseEffect çağrısı ile onu döndürülen JSX’inize hâlâ ekleyebilirsiniz. Bu, React’e onu renderdan yani işlemeden sonra, yan etkilere izin verildiğinde çalıştırmasını söyler. Ancak, bu yaklaşım son çareniz olmalıdır.

Mümkün olduğunda, mantığınızı yalnızca render ile ifade etmeye çalışın. Bunun sizi ne kadar ileri götürebileceğine şaşıracaksınız!

Derinlemesine İnceleme

React neden saflığı önemsiyor?

Saf işlevler yazmak biraz alışkanlık ve disiplin gerektirir. Ama aynı zamanda harika fırsatların da kapısını açar:

  • Bileşenleriniz farklı bir ortamda, örneğin sunucuda çalışabilir! Aynı girdiler için aynı sonucu döndürdüklerinden, bir bileşen birçok kullanıcı isteğine hizmet edebilir.
  • Girişleri değişmeyen bleşenleri render etmeyi atlayarak performansı artırabilirsiniz. Bu güvenlidir çünkü saf işlevler her zaman aynı sonuçları döndürür, bu nedenle önbelleğe alınmaları güvenlidir.
  • Derin bir bileşen ağacı render edilirken ortasında bazı veriler değişirse, React, zaman aşımına uğramış enderi bitirmek için zaman kaybetmeden işlemeyi yeniden başlatabilir. Saflık, herhangi bir zamanda hesaplamayı durdurmayı güvenli hale getirir.

İnşa ettiğimiz her yeni React özelliği, saflıktan yararlanır. Veri toplamadan animasyonlara ve performansa kadar, bileşenleri saf tutmak React paradigmasının gücünü açığa çıkarır.

Özet

  • Bir bileşen saf olmalıdır, yani:
    • Kendi işine bakar. İşlemeden önce var olan hiçbir nesneyi veya değişkeni değiştirmemelidir.
    • Aynı girdiler, aynı çıktılar. Aynı girdiler verildiğinde, bir bileşen her zaman aynı JSX’i döndürmelidir.
  • Oluşturma herhangi bir zamanda gerçekleşebilir, bu nedenle bileşenler birbirinin oluşturma sırasına bağlı olmamalıdır.
  • Bileşenlerinizin render için kullandığı girdilerin hiçbirini mutasyona uğratmamalısınız. Buna props, state ve context dahildir. Ekranı güncellemek için, önceden var olan nesneleri değiştirmek yerine state “oluşturun”.
  • Döndürdüğünüz JSX’te bileşeninizin mantığını ifade etmeye çalışın. “Bir şeyleri değiştirmeniz” gerektiğinde, bunu genellikle bir olay yöneticilerinde yapmak isteyeceksiniz. Son çare olarak, useEffect’i kullanabilirsiniz.
  • Saf fonksiyonlar yazmak biraz pratik gerektirir, ancak React’in paradigmasının gücünü açığa çıkarır.

Problem 1 / 3:
Bozuk bir saati düzelt

Bu bileşen, <h1>’in CSS class’ını gün esnasında, gece yarısından sabah 6’ya kadar "night" ve diğer tüm zamanlarda ise "day" olarak ayarlamaya çalışıyor. Ancak, bu işe yaramıyor. Bu bileşeni düzeltebilir misiniz?

Bilgisayarın saat dilimini geçici olarak değiştirerek çözümünüzün çalışıp çalışmadığını doğrulayabilirsiniz. Geçerli saat gece yarısı ile sabah altı arasında olduğunda, saat ters renklere sahip olmalıdır!

export default function Clock({ time }) {
  let hours = time.getHours();
  if (hours >= 0 && hours <= 6) {
    document.getElementById('time').className = 'night';
  } else {
    document.getElementById('time').className = 'day';
  }
  return (
    <h1 id="time">
      {time.toLocaleTimeString()}
    </h1>
  );
}