Published on

React.js'te State Mantığı

Authors

Nesne yönelimli paradigma'nın faydalarından biriside bildiğimiz gibi her sınıfın kendi veri elemanına sahip olabilmesidir. Bu veri elemanları istenirse sonradan sınıf içindeki metotlarla veya direkt erişim sağlanarak değiştirilebilir veya okunabilir. Kısacası veri elemanlarımız OOP'de küçük birer depo gibidirler.

class Car{
    constructor() {
      this.brand = 'BMW'; //Araba sınıfının marka ismini tutan veri elemanı
    }
    //..
}

Eğer bir UI teknolojisi geliştiriyorsanız veya kullanıyorsanız doğal olarak datanız ve arayüzünüzün birbirine bağımlı ve dinamik olmasını istersiniz. Bir yapının, veriniz değiştiğinde otomatik olarak UI'daki ilgili alanları güncellemesini yani tekrar render işlemini yapmasını istersiniz. İşte React.js'te UI'daki verileri dinamik olarak güncellemek için State denilen yapılar geliştirilmiş.

Kısacası bu yapıları kullandığımızda her doğru yapılmış State değişikliği UI'ı güncelleyecektir. Tahmin edileceği üzere bu güncelleme işlemini React otomatik olarak yapar.

React 16.8'den önceki versiyonlarda Class komponentleri ile geliştirme yaptığımızda React komponentinizi React.Component'ten extend ediyoruz ve sınıf içerisindeki State ihtiyacımızı oluşturduğumuz sınıfın veri elemanlarından karşılıyoruz.

Class komponentler ile uygulama geliştirdiğimizde aşağıdaki örnekteki gibi bir sınıf oluşturup state'leri bir veri elemanı olarak saklıyoruz. State'i değiştirmek istediğimizde ise this.setState metodu ile bu değişikliği yapıyoruz.

import React from "react";
import { render } from "react-dom";

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  handeClick = () => {
    return this.setState({ count: this.state.count + 1 });
  };

  render() {
    return (
      <div>
        <button onClick={this.handeClick}>Increase</button>
        <p>{this.state.count}</p>
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));

State'leri yöneten React olduğu için onun bize sunduğu fonksiyonları kullanıp bu güncellemeyi yapmalıyız ki React değişiklik olduğunu anlayabilsin ve güncellemeleri düzgün yapsın. Aşağıdaki kullanım gibi bir kullanım UI'ı güncellemeyecektir. Çünkü setState çalışmadı ve React değişikliği algılamadığı için render işlemini tekrar yapamadı.

handeClick = () => {
  return this.state = { count: 1 }; // ⚠️ Yanlış kullanım
};

React 16.8'den sonra React projesi Fonksiyonel Paradigma'ya destek vermeye başladı. Hiç sınıf kullanmadan komponentler oluşturabilmeliydik. Peki ama sınıflar olmadan State'leri nerede saklayacaktık?

Neyseki React ekibi useState ve diğer ihtiyacımız olan Hook'larıda beraberinde duyurdu 🙃

useState Hook'u aşağıdaki örnekteki gibi React kütüphanesinden çağırdığımız bir fonksiyon. Bu kullanım sayesinde Class'lardaki gibi veri elemanlarına ihtiyacımız olmadan veya herhangi bir Class'tan extend etmeye gerek kalmadan çok basit bir şekilde fonksiyonlar sayesinde yukarıdaki örnekteki ile aynı işlemi gerçekleştirdik. Magnificent!

import React from 'react';

const App = () => {
    const [state, setState] = React.useState(0);

    return (
        <div className="App">
            <p>You clicked {state} times</p>
            <button onClick={() => setState(state + 1)}>increase</button>
        </div>
    );
}

Şimdi gelin kendi useState Hook'umuzu yazalım 🙂

JavaScript'i güçlü yapan özelliklerden biriside hiç şüphesiz Closures. Bizde kendi useState Hook'umuzu yazarken bu özellikten faydalanacağız. Kısacası yazının bundan sonraki bölümünü anlamanız için Closures konseptini biliyor olmalısınız. Çok zor bir konsept değil ama eğer ilk defa duyuyorsanız MDN'in şu dokümantasyonunu inceleyip yazıya devam etmenizi tavsiye ederim.

Öncelikle LiteReact isimli kendi React modülümüzü oluşturalım. LiteReact içerisinde globalState isimli bir değişken oluştularım. useState fonksiyonunu LiteReact içerisine ekleyelim. Hatırlayalım React.useState ile State oluşturuyorduk. Bizde LiteReact.useState ile kendi State'lerimizi oluşturacağız.

const LiteReact = (() => {
  let globalState;

  const useState = (init) => {
    globalState = init;
    //..
  }
  
  return { useState };
})();

Initializer fonksiyonumuzu yazdık şimdi sırada State'leri update edecek setState fonksiyonunu ve State'e ihtiyacımız olduğunda çağırabileceğimiz getState fonksiyonunu yazalım.

const LiteReact = (() => {
  let globalState;

  const useState = (init) => {
    globalState = init;

    const setState = (newState) => {
      globalState = newState;
    };
    const getState = () => {
      return globalState;
    };

    return [getState, setState];
  }

  return { useState };
})();

useState fonksiyonu getState ve setState fonksiyonunu geri dönmeli. Aynı şekilde LiteReact'de useState fonksiyonunu geri döndürmeli.

const LiteReact = (() => {
  let globalState;

  const useState = (init) => {
    globalState = init;

    const setState = (newState) => {
      globalState = newState;
    };
    const getState = () => {
      return globalState;
    };

    return [getState, setState];
  }

  return { useState };
})();

function App() {
  const [getCount, setCount] = LiteReact.useState(0);

  setCount(getCount() + 1);
  console.log(getCount()); // 1
  setCount(getCount() + 1);
  console.log(getCount()); // 2
  setCount(getCount() + 1);
  console.log(getCount()); // 3
}

App();

Yazdığımız useState fonksiyonunu bir başka fonksiyon içerisinden çağırıp bir State oluşturduğumuzda State'leri getCount ile çağırdığımızı setCount ile değiştirdiğimizi görebilirsiniz. Fark ettiğiniz üzere JavaScript'in Closures özelliği sayesinde veri kaybolmadı bu sayede State benzeri bir yapı kurulmuş oldu.

Tabii ki normalde getState fonksiyonu yerine state isimli bir değişken olması gerekiyor. Eğer o şekilde çalışan useState hook'unun nasıl çalıştığını merak ediyorsanız aşağıdaki makaleyi inceleyebilirsiniz.

https://www.netlify.com/blog/2019/03/11/deep-dive-how-do-react-hooks-really-work

Bütün kodları aşağıdaki CodeSandbox uygulamasında bulabilirsiniz. Konsolu açmayı unutmayın 😁

Bu yazıya ilham kaynağı olduğu için Shawn Wang'a teşekkürler.

Thanks to Shawn Wang for inspiring this article. You can follow him on Twitter @swyx