Vanilla.js ile Redux

Merhabalar, ilk makalemde vanilla.js ile redux altyapısından bahsedeceğim.

Vanilla.js nedir?

Ordan bakınca, sanki bir javascript framework’ü gibi duruyor. Ancak bu bildiğimiz javascript. Her uyandığımız gün, farklı bir javascript framework’ü çıkmaz ise moralsiz başlıyoruz güne. Bununla dalga geçmek isteyen pure javascriptçi arkadaşlarımızın biz yeni başlayanlara yaptığı ufak bir şaka 🙂

Redux nedir?

Redux, Dan Abramov ve Andrew Clark abilerimizin oluşturduğu bir state management kütüphanesidir. Redux evrensel bir yapıdadır. Yani sadece javascript ile özdeşleştirilemez. Birçok teknolojide aktif olarak kullanabilirsiniz(en azından mantığını).

Redux ile birlikte, hem verilerin saklanmasını hem de UI kısmının yönetimini sağlayabilirsiniz.

Yani karmaşık yapılarda redux kullanmamak, usta developerların işi. Birazcık cambaz olmak lazım. Diğer biz çırak geliştiricilerin redux kaçınılmazı gibi duruyor.

Çalıştığım firmada, bir proje için önyüz teknolojisinde React.js kullanma kararı alınmıştı. Peki birden çok component ile birlikte, state taşımaları ve yönetimi nasıl sağlanacaktı. Tabiki redux. Şimdi redux’un kavramlarından bahsedelim.

Redux’un 3 ana kavramı var. (Ateş,su,toprak,tahta)

1-Store
2-Reducer
3-Action

1-Store: Redux frameworkü ile birlikte oluşturacağımız verilerin depolanacağı alandır. Bu store tektir. Başka store’a ihtiyacımız olmayacak. Aslında bütün componentlerin kullanabileceği bir değişken havuzu diyebiliriz. Biz laf aramızda, bu store’a global state diyelim..

2-Action: Action kısmı ise, UI kısmından tetiklenen kısımdır. Bu action ile birlikte Reducer aracılığı ile global state’deki hangi değişkenin güncellemesi gerektiğini belirleyen bir araç. Hangi değişkenin güncelleneceği “type” ile verilirken, değiştirelecek veriyi ise “payload” ile taşır.

3-Reducer: Reducer ise, actiondan gelen verileri filtreleyerek global state de belirtilen veriyi güncellememiz için gerekli kısım.

Şimdi abicim, ne üfürüyorsun kod göster kod!

Evet, aslında vanilla.js seçmek istememin sebebi, redux mantığını javascript ile tam hazmetmek. Şu an reactjs-angularjs-vuejs ile giriş yapsaydık, onları anlatmaktan redux anlatamazdık. Ben react ile ilk kullanmaya çalıştığımda çok zorlanmıştım. Vanilla.js ile biraz daha net oluştu kavramlar.

Dipnot: VNGRS firmasının düzenlediği A day to React eğitiminde güzel bir sunum yapan M. Hazar Artuner‘in yaptığı sunumda kullandığı kodları kullanacağım. Kendisine teşekkür ederim.

index.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Redux with Vanilla JS</title>
        <style>
            body, button {
                font-size: 50px;
            }
            button {
                background: #222;
                color: #fff;
            }
            span {
                padding: 0 20px;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <button id="decrease">-</button>
            <span id="count">0</span>
            <button id="increase">+</button>
        </div>
    </body>
</html>

Yukarı da index.html’i paylaştım. Aslında basit iki buton ile yürütülen, sayaç yapacağız 🙂

Şimdi ne olacak?

decrease id’li butona bastığım zaman sayaçtan sayı eksilecek, increase id’li butona bastığım zaman sayaçtan sayı artacak. Basit bir örnek, ama burada redux yapısını iyi kavramamız için verilmiş bir örnek.

Peki redux tarafında ne olacak?

Burda basılan butonu, action’a dispatch edeceğim. O da kendi type’i ile, reducer’i tetikleyecek; reducer da bu type’ a göre store da güncelleme yapacak.

action

const increaseCounter = {
  type: 'INCREASE',
  payload: 1
};

const decreaseCounter = {
  type: 'DECREASE',
  payload: 1
};

decrease id’li butona basıldığında decreaseCounter adlı action’ımız çalışarak, type olarak ‘DECREASE’ gönderecek(reducer anlasın diye) ve ne kadarlık sayaçtan değer düşmek istediğimizi reducer’a iletecek.

reducer ve store

// region REDUCER
function counterReducer(state, action) {
  switch(action.type) {
    case 'INCREASE':
      return {
        count: state.count + action.payload,
      };

    case 'DECREASE':
      return {
        count: state.count - action.payload,
      };

    default:
      return state;
  }
}
// endregion

// region STORE
const initialState = {
  count: 0,
};

const store = createStore(counterReducer, initialState);
// endregion

action ilgili type’a göre reducer fonksiyonunu tetikliyor. O da global de tutulan, count değişkenini güncelliyor.

Bu action.js her yerden erişilebilir olduğu için, state yönetimini bütün componentlere taşınabilir hale getiriyor.

Bütün yapıyı paylaşıyorum.

import { createStore } from 'redux';

// region ACTIONS
const increaseCounter = {
  type: 'INCREASE',
  payload: 1
};

const decreaseCounter = {
  type: 'DECREASE',
  payload: 1
};
// endregion

// region REDUCER
function counterReducer(state, action) {
  switch(action.type) {
    case 'INCREASE':
      return {
        count: state.count + action.payload,
      };

    case 'DECREASE':
      return {
        count: state.count - action.payload,
      };

    default:
      return state;
  }
}
// endregion

// region STORE
const initialState = {
  count: 0,
};

const store = createStore(counterReducer, initialState);
// endregion

// region APPLICATION CODE
const decreaseEl = document.getElementById('decrease');
const increaseEl = document.getElementById('increase');
const countEl = document.getElementById('count');

function render() {
  const state = store.getState();

  countEl.innerText = state.count;
}

render();

store.subscribe(render);

decreaseEl.addEventListener('click', () => store.dispatch(decreaseCounter));
increaseEl.addEventListener('click', () => store.dispatch(increaseCounter));
// endregion

Umarım anladığımı doğru aktarabilmişimdir. Sorularınız veya eleştirileriniz için, yorum kısmını veya about kısmında bulunan mail adresimi kullanabilirsiniz.

Teşekkür: Bu yapıyı kafamıza vura vura öğreten, abim ve takım liderim Musa Karakelle‘ye ve kahrımı çeken usanmadan bu kavramları anlatan takım arkadaşım Ahmet Çetin‘e teşekkürlerimi iletiyorum.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir