Redux Nedir?
React’e başlangıç yapan birçok geliştirici, Redux’ın en azından adını duymuştur veya halihazırda Redux ile geliştirme yapıyordur. Bu yazımızda sizlere hemen her JavaScript uygulamasında neden Redux’a gereksinim duyduğumuzu, Redux’ın ana elemanlarını ve bir demoyu aktaracağım. Öncelikle React uygulamalarında karşılaşılan problem nedir ve Redux bunu nasıl çözüyor ona değinelim.
React Uygulamalarında Karşılaşılan Temel Problem
React ile kolayca başlayıp ilerlettiğiniz projenizde sonraki aşamalarda uygulama karmaşıklaştıkça, iç içe bileşenler arasındaki state yönetimi gittikçe güçleşir. Hangi verinin hangi bileşenden geldiğinin takibi zorlaşır. Aşağıdaki örneği ele alalım:
Örneğimizde 4 adet bileşen bulunuyor. React’te tek yönlü veri iletimi söz konusu olduğundan dolayı, bu bileşenler ihtiyaç duydukları veriyi bir üst bileşenden almak zorunda. 4. bileşenin ihtiyaç duyduğu veri 2. bileşende ve 3. bileşende bulunuyor. 4. bileşen 2. bileşendeki state’e direkt olarak erişemediği için 3. bileşenden alma gereksinimi doğuyor. 3. bileşen de hiç gerekmediği halde 2. bileşenin state’ini içermek durumunda kalıyor. Bu nedenle de uygulamadaki state yönetimi güçleşiyor.
Peki Neden Redux’a İhtiyacımız Var?
Redux, veri erişiminin tek bir yerden yönetilmesinin yanında, yukarıda anlattığımız state aktarımından doğan problemi de çözmek üzere tasarlanmıştır. Redux’ta bütün state verileri store adı verilen bir yerde tutulur. Böylece üst seviye bileşenden alt seviye bileşene state aktarımına gerek kalmaz. Her bileşen, uygulama state’ine store aracılığıyla erişim sağlar. Veriler, tek bir sorgulama kaynağından (store) getirildiği için, hangi state nereden gelmiş gibi bir veri takibine gerek kalmamış olur. Bu sayede, oluşan kod Redux ile daha temiz hale gelmiş olur.
Redux’ın Üç Silahşörlerini Tanıyalım: Action, Reducer ve Store
Redux’ta veri aktarımı bu 3 eleman vasıtasıyla gerçekleştirilir ve UI’a sunulur.
Dilerseniz öncelikle Action’ların ne olduğuna yakından bakalım:
Action
Action’lar uygulama içerisinden store’a iletilen değişkenlerin bilgilerini tutarlar. dispatch(action)
fonksiyonu ile store’a yazılırlar. Action’lar en sade şekilde JavaScript nesneleridir ve içerisinde mutlaka type
isimli bir string değişkeni tutarlar. type
bilgisi tıpkı bir değişken adı gibi, store’a yazılacak verinin tipini belirler. Örnek bir action aşağıdaki gibidir:
{ type: "YAZI_EKLE", baslik: "Redux ile Uygulamalarınızın State'ini Yönetin" }
Uygulama büyüdükçe çok fazla action ortaya çıkacağı için type ifadelerini sabit değişkene atamakta yarar var.
const YAZI_EKLE = 'YAZI_EKLE' const YAZI_KALDIR = 'YAZI_KALDIR' { type: YAZI_EKLE, baslik: "Redux ile Uygulamalarınızın State'ini Yönetin" } { type: YAZI_KALDIR, indeks: 42 }
Ayrıca action listesi de derli toplu olması açısından harici bir modüle taşınarak kullanılabilir.
import { YAZI_EKLE, YAZI_KALDIR } from '../actionTypes'
YAZI_EKLE ve YAZI_KALDIR action’ları haricinde kullanıcının bir yazının inceleme için onaya sunulması durumunu düşünerek de bir action oluşturmamız gerekmektedir. Array içerisinde bulunan ilgili yazıya erişim yapmak için indeks
özelliğini kullanabiliriz. Fakat gerçek bir uygulamada her bir yazının unique bir id’ye sahip olacağını düşünürsek, id
gibi bir alan kullanmamız daha doğru olur. Şimdilik, örnek için bu durumu basitleştirelim.
{
type: ONAY_DURUMUNU_DEGISTIR,
indeks: 5
}
Her bir action için yazi
nesnesinin tamamını göndermek yerine, ilgili yazının bulunacağı konumu belirten indeks
alanını göndererek basitliği sağlamış oluyoruz.
Son olarak mevcut durumda listelenen yazıların filtrelenmesi için de bir action ekleyelim:
{ type: LISTELEME_FILTRESI_ATA, filtre: ONAYA_SUNULANLARI_GOSTER }
Action Üreticileri (Action Creators)
Action üreticileri adından da anlaşılacağı gibi action üreten fonksiyonlardır. Bu fonksiyonlar tanımlanan bir action’ı return eder.
function yaziEkle(baslik) { return { type: YAZI_EKLE, baslik } }
Bu sayede taşınabilir ve kolayca test edilebilir hale gelirler. Store’a bir action dispatch etmek için ise ilgili action üreticisi çağırılır.
dispatch(yaziEkle(baslik)) dispatch(onayDurumunuDegistir(indeks))
Alternatif olarak, otomatik dispatch işleminin gerçekleştirilmesi için bağlı action üreticileri (bound action creator
) oluşturulabilir.
const boundYaziEkle = baslik => dispatch(yaziEkle(baslik)) const boundOnayDurumunuDegistir = indeks => dispatch(onayDurumunuDegistir(indeks))
Böylece direkt olarak çağrılabilirler:
boundYaziEkle(baslik) boundYaciyiIncelemeyeGonder(indeks)
dispatch()
fonksiyonuna, store nesnesi üzerinden store.dispatch()
şeklinde kullanımı ile direkt olarak erişilebilir. Fakat React uygulamalarında daha optimize render işlemleri için react-redux kütüphanesinin sunduğu connect()
yardımcı fonksiyonu kullanılır. Çoklu action üreticilerinin tek seferde dispatch edilmesi için ise bindActionCreators()
metodundan yararlanılabilir.
actions.js
dosyasının son hali aşağıdaki gibi olacaktır:
// Action tipleri export const YAZI_EKLE = 'YAZI_EKLE' export const ONAY_DURUMUNU_DEGISTIR = 'ONAY_DURUMUNU_DEGISTIR' export const LISTELEME_FILTRESI_ATA = 'SET_VISIBILITY_FILTER' // Diğer sabitler export const ListelemeFiltreleri = { HEPSINI_GOSTER: 'HEPSINI_GOSTER', ONAYA_SUNULANLARI_GOSTER: 'ONAYA_SUNULANLARI_GOSTER', YAYINDAKILERI_GOSTER: 'YAYINDAKILERI_GOSTER' } // Action üreticileri export function yaziEkle(baslik) { return { type: YAZI_EKLE, baslik } } export function onayDurumunuDegistir(indeks) { return { type: ONAY_DURUMUNU_DEGISTIR, indeks } } export function listelemeFiltresiAta(filtre) { return { type: LISTELEME_FILTRESI_ATA, filtre } }
Reducer
Reducer’lar, store’a gelen action sonucunda uygulamanın state’inin nasıl değiştirileceğini belirler. Action’ların ise uygulamaya sadece ne olacağını söylerler, uygulamanın durumunun nasıl değiştirileceğini bilemezler.
State’in Tasarlanması
Redux’ta uygulamanın state’i tek bir nesne olarak saklanır. Kod yazmaya başlamadan önce bu nesnenin tasarımının üzerinde biraz durup düşünmekte fayda vardır. Örneğin bir blog uygulamasında iki farklı öğeyi saklamak isteyebiliriz:
- Yazıların listede görüntülenmesi için seçilen filtre öğesi.
- Blog yazılarının listesi.
Bu bilgilerin yanında, doğal olarak UI durumunun da saklanmasını isteyebiliriz. Fakat bu noktada verilerin, UI state’inden ayrı olarak tutulması gerekir. Örnek blog uygulaması state’i:
{ listelemeFiltresi: 'HEPSINI_GOSTER', yazilar: [ { baslik: "Redux ile Uygulamalarınızın State'ini Yönetin", onaylandi: false, }, { baslik: 'React için 30 Arayüz Kütüphanesi', onaylandi: true } ] }
Action’ların Yönetimi
State’i oluşturduğumuza göre artık state ve action’ı alıp yeni bir state üretecek reducer’ı yazabiliriz. Reducer temel olarak bir JavaScript fonksiyonudur ve önceki state ile action’ı alarak sonraki state’i üretir.
(oncekiState, action) => yeniState
Bu fonksiyona reducer adının verilmesinin sebebi, gerçekten de Array.prototype.reduce(reducer, ?initialValue)
fonksiyonu içerisinde reducer
kısmına geçirilecek fonksiyon olmasıdır. Reducer fonksiyonu, pure fonksiyon olmalıdır (Pure fonksiyonlar aynı input için aynı output değeri üreten fonksiyonlardır ve kendi scope’u dışındaki bir değişkene erişmezler ve bu değişkeni değiştirmezler. Fazlası için: nicoespeon.com). Reducer içerisinde yapılmaması gerekenleri aşağıdaki şekilde sıralayabiliriz:
- Parametre olarak geçilen argümanlar değiştirilmemelidir.
- API çağrıları veya sayfa geçişleri gibi yan etki yapan işlemler gerçekleştirilmemelidir.
Date.now()
veyaMath.random()
gibi pure olmayan fonksiyonlar çağrılmamalıdır.
Temel olarak bir reducer pure olması gerektiği için sadece hesaplama yapılan işlemleri içermelidir. API çağrımı gibi işlemler reducer’ın sorumluluğu değildir.
Reducer’ı pekiştirmek için önceden oluşturduğumuz action’lar ile reducer’ımızı yazalım. Öncelikle başlangıç state’ini oluşturmamız gerekiyor. Başlangıçta bir state bulunmaduğu için, state’in undefined
olma durumunu için reducer’ımızı oluşturalım. Burada neredeyse boş bir state dönmemiz gerekiyor. Bunu nesne yönelimli dillerdeki constructor
gibi düşünebilirsiniz:
import { ListelemeFiltreleri } from '../actions' const baslangicState = { listelemeFiltresi: ListelemeFiltreleri.HEPSINI_GOSTER, yazilar: [] } function blogUygulamasi(state, action) { if (typeof state === 'undefined') { return baslangicState } // Şimdilik herhangi bir action üzerinde işlem yapmamız gerekmiyor. // Sadece başlangıç state'ini dönmemiz yeterli. return state }
blogUygulamasi()
fonksiyonunu ES6 varsayılan argümanlar özelliğini kullanarak da oluşturabiliriz. Bu sayede daha basit ve yalın bir kod elde etmiş oluruz.
function blogUygulamasi(state = baslangicState, action) { // Şimdilik herhangi bir action üzerinde işlem yapmamız gerekmiyor. // Sadece başlangıç state'ini dönmemiz yeterli. return state }
Şimdi istersek diğer action’larımızı ekleyebiliriz. LISTELEME_FILTRESI_ATA
action’ımız için reducer’da sadece state’in listelemeFiltresi
özelliğini değiştirmemiz yeterli olacaktır.
function blogUygulamasi(state = baslangicState, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return Object.assign({}, state, { listelemeFiltresi: action.filtre }) default: return state } }
Buradaki 2 kısım çok önemlidir:
- State’in direkt olarak değişmemesi gerektiği. Reducer’ın pure kalması için
Object.assign()
ile state’in bir kopyasını oluşturduk. EğerObject.assign(state, { visibilityFilter: action.filter })
şeklinde kullanırsak state’i yine değiştirmiş oluruz. Bu nedenle ilk parametre olarak boş bir nesne{}
atamamız önemlidir. Eğer Babel gibi bir transpiler kullanıyorsak, nesne yayma operatörünü (object spread operator) de kullanabiliriz.
function blogUygulamasi(state = baslangicState, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return { ...state, listelemeFiltresi: action.filtre } default: return state } }
- switch-case’in default adımında mevcut state’in döndürülmesi. Eğer bilinmeyen bir action gelmişse mevcut state’i döndürmek önemlidir.
Diğer Action’ların Ele Alınması
LISTELEME_FILTRESI_ATA
action’ında yaptığımız gibi YAZI_EKLE
ve ONAY_DURUMUNU_DEGISTIR
action’ları için case ifadelerini eklememiz gerekiyor. Öncelikle YAZI_EKLE
‘yi ele alalım:’
import { YAZI_EKLE, ONAY_DURUMUNU_DEGISTIR, LISTELEME_FILTRESI_ATA, ListelemeFiltreleri } from './actions' ... function blogUygulamasi(state = baslangicState, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return Object.assign({}, state, { listelemeFiltresi: action.filtre }) case YAZI_EKLE: return Object.assign({}, state, { yazilar: [ ...state.yazilar, { baslik: action.baslik, onaylandi: false } ] }) default: return state } }
Daha önce de yaptığımız gibi, state’i veya state’in alanlarını değiştirmiyoruz bunun yerine yeni oluşturduğumuz nesneleri dönüyoruz. yazilar
nesnesi, eski oluşturduğumuz yazilar
nesnesine action’dan aldığımız yeni yazi nesnesinin parametrelerinin eklenmesi ile oluşuyor.
Son olarak ONAY_DURUMUNU_DEGISTIR
action’ı kodlayalım:
case ONAY_DURUMUNU_DEGISTIR: return Object.assign({}, state, { yazilar: state.yazilar.map((yazi, indeks) => { if (indeks === action.indeks) { return Object.assign({}, yazi, { onaylandi: !yazi.onaylandi }) } return yazi }) })
Burada map()
fonksiyonu ile yazilar
üzerinde dolaşarak sadece ilgili indeks’teki elemanın kopyasını oluşturarak onay durumunu değiştirdik. Eğer sürekli bu tarz kopyalama-atama tarzı işlemler ile uğraşıyorsanız Facebook’ın Immutable kütüphanesi işinize yarayabilir.
Reducer Fonksiyonunun Ayrıştırılması
Şu ana dek oluşturduğumuz blogUygulamasi reducer’ı aşağıdaki hale geldi:
function blogUygulamasi(state = baslangicState, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return Object.assign({}, state, { listelemeFiltresi: action.filtre }) case YAZI_EKLE: return Object.assign({}, state, { yazilar: [ ...state.yazilar, { baslik: action.baslik, onaylandi: false } ] }) case ONAY_DURUMUNU_DEGISTIR: return Object.assign({}, state, { yazilar: state.yazilar.map((yazi, indeks) => { if (indeks === action.indeks) { return Object.assign({}, yazi, { onaylandi: !yazi.onaylandi }) } return yazi }) }) default: return state } }
Gördüğümüz gibi listelemeFiltresi
değişkeni ile yazilar
array’i tamamen birbirinden farklı olarak değiştiriliyor. Sadece yazilar
array’inden sorumlu ve bu array’i değiştirecek diğer bir reducer oluşturabiliriz. Böylelikle yazilar
, ile listelemeFiltresi
nin değiştirilmesi işlemleri farklı reducer’ların sorumluluğunda olacaktır. yazilar
array’ini state parametresi olarak alacak şekilde reducer’ı ayıralım.
function yazilar(state = [], action) { switch (action.type) { case YAZI_EKLE: return [ ...state, { baslik: action.baslik, onaylandi: false } ] case ONAY_DURUMUNU_DEGISTIR: return state.map((yazi, indeks) => { if (indeks === action.indeks) { return Object.assign({}, yazi, { onaylandi: !yazi.onaylandi }) } return yazi }) default: return state } } function blogUygulamasi(state = baslangicState, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return Object.assign({}, state, { listelemeFiltresi: action.filtre }) case YAZI_EKLE: return Object.assign({}, state, { yazilar: yazilar(state.yazilar, action) }) case ONAY_DURUMUNU_DEGISTIR: return Object.assign({}, state, { yazilar: yazilar(state.yazilar, action) }) default: return state } }
Görüldüğü gibi yazilar()
fonksiyonu da state
parametresi alıyor. Fakat bu parametre bir array tipinde olabiliyor. Böylece blogUygulamasi()
state’in sadece yazilar
array’i ile ilgili kısmını yazilar()
fonksiyonuna veriyor ve bu fonksiyon sadece array’i değiştirmeyi biliyor. Bu duruma reducer kompozisyonu
(reducer composition) adı verilir. Redux uygulamalarında reducer kompozisyonu temel geliştirim şablonlarından biridir.
Oluşturduğumuz reducer kompozisyonunu biraz daha ileri götürerek sadece listelemeFiltresi
değişkeni için bir reducer yazabilir miyiz ona bir bakalım.
HEPSINI_GOSTER
değişkeninin ListelemeFiltreleri
nesnesinden alınması için ES6 Object Destructuring özelliğini kullanabiliriz:
const { HEPSINI_GOSTER } = ListelemeFiltreleri
Sonrasında listelemeFiltresi
reducer’ı aşağıdaki hale gelecektir:
function listelemeFiltresi(state = HEPSINI_GOSTER, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return action.filtre default: return state } }
Artık ana reducer’ımız olan blogUygulamasi
‘nı state’in farklı bölümlerini değiştirecek ve tekil bir nesne haline getirecek şekilde düzenleyebiliriz:
function yazilar(state = [], action) { switch (action.type) { case YAZI_EKLE: return [ ...state, { baslik: action.baslik, onaylandi: false } ] case ONAY_DURUMUNU_DEGISTIR: return state.map((yazi, indeks) => { if (indeks === action.indeks) { return Object.assign({}, yazi, { onaylandi: !yazi.onaylandi }) } return yazi }) default: return state } } function listelemeFiltresi(state = HEPSINI_GOSTER, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return action.filtre default: return state } } function blogUygulamasi(state = {}, action) { return { listelemeFiltresi: listelemeFiltresi(state.listelemeFiltresi, action), yazilar: yazilar(state.yazilar, action) } }
Farkedileceği üzere her bir reducer’ı, global state’teki sadece kendisi ile ilgili olan kısımları değiştirecek şekilde kodladık. state
parametresi her bir reducer için farklı bir tipte oluşturuldu ve sadece ilgili tip ile işlem yapacak hale getirildi. Reducer’lar bu haliyle bile oldukça yalın ve sade bir hale geldi. Uygulamanız büyüdükçe reducer’ların her birini ayrı ayrı js dosyalarına yazabilirsiniz. Redux, ana reducer olan blogUygulamasi()
‘nın yaptığı işleri daha basit hale getiren combineReducers() metodunu içerir. Bu sayede blogUygulamasi()
reducer’ını aşağıdaki gibi yazabiliriz:
import { combineReducers } from 'redux' const blogUygulamasi = combineReducers({ listelemeFiltresi, yazilar }) export default blogUygulamasi
Üstteki kod, aşağıdaki ile aynı çalışacaktır:
export default function blogUygulamasi(state = {}, action) { return { listelemeFiltresi: listelemeFiltresi(state.listelemeFiltresi, action), yazilar: yazilar(state.yazilar, action) } }
Ayrıca state’in farklı değişkenlerini farklı isimlerle çağıracak fonksiyonları aşağıdaki şekilde combine edebiliriz:
const reducer = combineReducers({ a: aDegiskeniIleBirseylerYap, b: bYiCalistir, c: c })
Aşağıdakine denk olacaktır:
function reducer(state = {}, action) { return { a: aDegiskeniIleBirseylerYap(state.a, action), b: bYiCalistir(state.b, action), c: c(state.c, action) } }
combineReducers()
fonksiyonunun yaptığı iş, ilgili değişken adlarına göre state’in ilgili bölümlerini çağıran reducer fonksiyonlarını tutacak bir ana fonksiyon oluşturmaktır. Daha sonra reducer’lardan aldığı değeri tek bir nesneye dönüştürecek şekilde combine eder.
reducers.js’in son hali aşağıdaki gibi olacaktır:
import { combineReducers } from 'redux' import { YAZI_EKLE, ONAY_DURUMUNU_DEGISTIR, LISTELEME_FILTRESI_ATA, ListelemeFiltreleri } from './actions' const { HEPSINI_GOSTER } = ListelemeFiltreleri function yazilar(state = [], action) { switch (action.type) { case YAZI_EKLE: return [ ...state, { baslik: action.baslik, onaylandi: false } ] case ONAY_DURUMUNU_DEGISTIR: return state.map((yazi, indeks) => { if (indeks === action.indeks) { return Object.assign({}, yazi, { onaylandi: !yazi.onaylandi }) } return yazi }) default: return state } } function listelemeFiltresi(state = HEPSINI_GOSTER, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return action.filtre default: return state } } const blogUygulamasi = combineReducers({ listelemeFiltresi, yazilar }) export default blogUygulamasi
Store
Action’ları, state’ler üzerinde “Ne yapılacağını”, reducer’ları ise action’ları temel alarak ilgili state’in “Nasıl güncelleneceğini” belirtecek şekilde oluşturduk. Store nesnesi ise bu iki elemanı bir araya getirecek bir yapı sağlar. Store’un sorumlulukları aşağıdaki şekilde sıralanabilir:
- Uygulama state’inin tutulması,
getState()
metodu ile state’e erişim sağlaması,dispatch(action)
metodu ile state’in güncellemesini sağlaması,subscribe(listener)
metodu ile listener’ları bağlaması,subscribe(listener)
metodundan dönen listener’ları çıkarmasıdır.
Bir reducer uygulamasında sadece tek bir store olmak zorundadır. Verilerin yönetilme biçimini sağlamak için birden fazla store oluşturmak yerine reducer kompozisyonu kullanmak gereklidir.
Eğer ana bir reducer’ınız varsa bu reducer’dan store oluşturmak çok kolaydır. combineReducers()
fonksiyonunu çağıran reducer, createStore()
fonksiyonuna parametre olarak geçilebilir:
import { createStore } from 'redux' import blogUygulamasi from './reducers' const store = createStore(blogUygulamasi)
Başlangıç state’i, createStore()
‘un ikinci parametresi olarak atanabilir. Bu sayede eğer sunucuda çalışan bir Redux uygulamanız varsa, istemcide çalışan state ile sunucudaki state’i eşleştirebilirsiniz:
const store = createStore(blogUygulamasi, window.STATE_SUNUCU)
Action’ların Store’a Aktarılması (Dispatch)
Store’u da oluşturduğumuza göre, console üzerinden uygulamamızı test edebiliriz.
<!DOCTYPE html> <html> <head> <title>Redux - Blog</title> <script src="https://unpkg.com/redux@latest/dist/redux.min.js"></script> </head> <body> <script> // Action tipleri const YAZI_EKLE = 'YAZI_EKLE'; const ONAY_DURUMUNU_DEGISTIR = 'ONAY_DURUMUNU_DEGISTIR'; const LISTELEME_FILTRESI_ATA = 'SET_VISIBILITY_FILTER'; // Diğer sabitler const ListelemeFiltreleri = { HEPSINI_GOSTER: 'HEPSINI_GOSTER', ONAYA_SUNULANLARI_GOSTER: 'ONAYA_SUNULANLARI_GOSTER', YAYINDAKILERI_GOSTER: 'YAYINDAKILERI_GOSTER' }; HEPSINI_GOSTER = ListelemeFiltreleri.HEPSINI_GOSTER; // Action üreticileri function yaziEkle(baslik) { return { type: YAZI_EKLE, baslik } } function onayDurumunuDegistir(indeks) { return { type: ONAY_DURUMUNU_DEGISTIR, indeks } } function listelemeFiltresiAta(filtre) { return { type: LISTELEME_FILTRESI_ATA, filtre } } // Reducer'lar function yazilar(state = [], action) { switch (action.type) { case YAZI_EKLE: return [ ...state, { baslik: action.baslik, onaylandi: false } ] case ONAY_DURUMUNU_DEGISTIR: return state.map((yazi, indeks) => { if (indeks === action.indeks) { return Object.assign({}, yazi, { onaylandi: !yazi.onaylandi }) } return yazi }) default: return state } } function listelemeFiltresi(state = HEPSINI_GOSTER, action) { switch (action.type) { case LISTELEME_FILTRESI_ATA: return action.filtre default: return state } } const blogUygulamasi = Redux.combineReducers({ listelemeFiltresi, yazilar }); // Store const store = Redux.createStore(blogUygulamasi) // Test console.log(store.getState()) // State her değiştiğinde ekrana loglanması // Görüldüğü gibi subscribe() metodu, console listener'ını çıkarmak için bir fonksiyon geri döndürmektedir. const unsubscribe = store.subscribe(() => console.log(store.getState()) ) // Action'ların store'a aktarılması store.dispatch(yaziEkle("Redux ile Uygulamalarınızın State'ini Yönetin")) store.dispatch(yaziEkle("React için 30 Arayüz Kütüphanesi")) store.dispatch(yaziEkle("DigitalOcean Üzerinde .NET Core MVC Uygulamanızı Çalıştırın")) store.dispatch(onayDurumunuDegistir(0)) store.dispatch(onayDurumunuDegistir(1)) store.dispatch(listelemeFiltresiAta(ListelemeFiltreleri.YAYINDAKILERI_GOSTER)) // state güncellemelerinden console.log()'un çıkarılması unsubscribe() </script> </body> </html>
Uygulamanın arayüzünü bile yazmadığımız halde davranışını belirlemiş olduk. Pure fonksiyonlar (reducer’lar) sayesinde uygulamanın herhangi bir kısmı için mock nesne oluşturmadan unit testler yazabiliriz.
Sonuç olarak
Redux, uygulama state’inin tek bir elden (store) kolayca yönetimini sağlıyor. React kullanılmasa dahi, Redux’ın her bir proje için çok önemli bir eleman teşkil ettiği görüşündeyim.
Eğer sizin de Redux hakkında sorularınız veya görüşleriniz varsa yorum kısmından bize yazabilirsiniz. Bir sonraki yazımızda görüşmek üzere.
4 Comments
ali yılmaz
13 Eylül 2018 at 21:13Çok güzel bir makale olmuş. Elinize sağlık. Yazılarınızın devamını bekliyoruz.
Zafer Ayan
15 Kasım 2018 at 21:29Teşekkür ederim.
emir gül
8 Kasım 2018 at 13:35Güzel bir yazı. Teşekkürler
Zafer Ayan
15 Kasım 2018 at 21:30Rica ederim. Teşekkürler.