Photo by David Menidrey on Unsplash

REACT STATE MANAGEMENT

Hangi Client State Management Yöntemi’ni Kullanmalıyım ? (Redux Connect)

Önceki yazılarda https://onurdayibasi.com/react-redux/ altında ClientState Management ile ilgili useState, useContext, Zustand ve Jotai konularından bahsettim.

Redux Toolkit’ te Zustand ile neredeyse aynı ve aynı sonuçları verecek. Tek farkı Provider Pattern blog yazımda anlattığım gibi uygulamanın başında App bileşeninizi Redux Provider ile sarmalayıp Store vermeniz gerekiyor. Zustand ise buna göre daha lightweight. Ama Rendering performansları, kod yazım yapıları neredeyse birbirine benzer. Zaten Zustand olabildiğince Redux benzereyerek ona bir leightweight alternatif oluşturmaya çalışıyor.

import { store } from './app/store';
import { Provider } from 'react-redux';

ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);

Benim üzerinde durmak istediğim ise Redux Connect yapısı. Büyük bir ihtimalle Hooks akımı ile bu HoC yapısıda yakında deprecated olacak fakat öncesinde bunun faydalarından diger useSelector veya slice üzerinden kullanımlara göre ne gibi faydası olduğu üzerinde duracağım.

Aslında HoC da Fonksiyonel Programlamanın temelinde olan High Order Function’lardan gelen bir kavram. Ve JS dilinde, kütüphanelerde sıkça kullanılıyor.

Peki Hook’lar Higher Order Component yerine geçecek bir kavram mı? 2023 dünyasında artık evet diyoruz. Fakat 2019 Eric Elliott yazmış olduğu aşağıdaki blog HOC ne gibi avantajları olduğunu görebilirsiniz.

Do React Hooks Replace Higher Order Components (HOCs)?

AVANTAJ ve DEZAVANTAJLARI

Peki buna göre biz yapımızı daha esnek nasıl yapabiliyorduk ?

React Connect ile yapacağımız yapının Avantajları ve Dezavantajları üzerinde duralım.

Dezavantajları

  • Hooks yapısı artık popüler ve HoC ortadan kaldırıyor veya kaldırdı.
  • Eğer bileşenleri tamamen ulaşacağı State uzak tutup ayrık yapıp başka storelara bağlamayacaksak mapStateToProps , mapDispatchToProps aynı fonksiyonları 2 defa yazmış oluyoruz..
  • Kodun okunabilirliği biraz azaliyor.

Avantajları Neler

  • Bileşen tamamen kendisine verilen props fonksiyonu ve parametreleri üzerinden çalışıyor. Bu yüzden daha dummy ve kendisine verileni biliyor sadece
  • Bu dışarı çıkarılan kısımda bileşenin kullanacağı verilerin hazılarnmasında veya dışarıdaki fonksiyonların çağrılmasındaki Logicler ara Container katmanlarına hapsediliyor ve bu kısımlarda başka Container yapıları ile değiştirilerek daha esnek bir kullanıma kavuşmuş oluyoruz.
  • Bileşenin esnekliği arttığı için büyük projelerde bileşenlerin tekrar tekrar yazımını ortadan kaldırıyor.

Aşağıdaki 3 örnekte Bu React Mimarisi üzerinden bu Container — Component kullanım yapısını anlatıyorum

React Örnek Proje Mimarisi
React Örnek Proje Mimarisi-2
React Örnek Proje Mimarisi-3

ÖRNEK

Mevcut yapıdaki örneğimize dönersek (Linki)

https://onurdayibasi.dev/state-redux-connect2

Kodu biraz inceleyelim. Öncelikle Redux Toolkit benzer bir şekilde Action ve Reducer Slice 2 Counter için tanımlamamız lazım aşağıda Counter 1 için olanı görüyorsunuz.

const INC_COUNTER1 = 'INC_COUNTER1';

export function incCounter1() {
return {
type: INC_COUNTER1,
payload: {},
};
}

const counter1InitialState = {
value: 0,
};

export default function counter1(state = counter1InitialState, action) {
switch (action.type) {
case INC_COUNTER1:
return {
...state,
value: state.value + 1,
};
default:
return state;
}
}

tabi combineReducer içerisinde bunları Store içerisine koymamız gerekiyor

//Reducers
const rootReducer = combineReducers({
counter1:counter1,
counter2:counter2,
});

export const store = createStore(rootReducer, applyMiddleware(promise, thunk));

ve bu store Provider Pattern blog yazısında anlattığım gibi App sarmalamak gerekiyor.

  <Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Router history={history}>
<App />
</Router>
</PersistGate>
</Provider>,

Not: Yukarıdaki kızım Zustand olmadığı için Zustand kullanımı ve öğrenme eğrisi Redux göre daha basit.

Şimdi Bileşenlerin olduğu kısımları gelelim. useSelector, useDispatch veya Slice yapılarını kullanmak yerine eski connect HOC yapısını kullanarak geliştireceğiz

Bu kısımda bileşenlerin Component ve Container olarak 2 kısma ayıracağımız yapıyı oluşturmamızı sağlayan Store kodları ve onlar üzerine kurulmuş fonksiyonlarımız bulunuyor.

import { incCounter1 } from 'store/counter1';
import { incCounter2 } from 'store/counter2';
import { connect } from 'react-redux';

const mapStateToPropsEmpty = () => () => ({});
const mapStateToPropsC1 = () => store => ({ counter: store.counter1 });
const mapStateToPropsC2 = () => store => ({ counter: store.counter2 });

const mapDispatchToPropsEmpty = () => {
return {}
};

const mapDispatchToPropsC1 = (dispatch) => {
return {
inc() {
dispatch(incCounter1());
}
}
};
const mapDispatchToPropsC2 = (dispatch) => {
return {
inc() {
dispatch(incCounter2());
}
}
};

Gelin bu fonksiyonları biraz inceleyelim;

mapStateToProps

mapStateToProps fonksiyonları store yer alan bir ilgilendiğimiz state değişince Comp props güncelleme gerçekleştirecektir.

Bizimde burada 3 tip böyle mapStateToProps ihtiyacımız olacak

  • mapStateToPropsEmpty: Container sahip Inc1 ve Inc2 düğmesinin değerin kendisini bilmesine gereksinmi yok yani bir props ihtiyaç duymuyorlar
  • mapDispatchToPropsC1: store => ({ counter: store.counter1 }); sadece C1 Value gösteren bileşen,
  • mapDispatchToPropsC2: store => ({ counter: store.counter2 }); sadece C2 Value gösteren bileşen,

mapDispatchToProps

mapDispatchToProps fonksiyonları store haber gönderip state güncelletmek istediğimiz fonksiyonlardır.

Aynı şekilde 3 tip dışarı ile konuşma mapDispatchToProps ihytiyacımız olacak

  • mapDispatchToPropsEmpty : ValueDisp böyle bir isteği bulunmuyor.
  • mapDispatchToPropsC1: birinci counter arttıracak dispatch işlemini soyutlayan fonksiyona ihtiyacı bulunuyor
  • mapDispatchToPropsC2:ikinci counter arttıracak dispatch işlemini soyutlayan fonksiyona ihtiyacı bulunuyor

Tüm bunları yaptıktan sonra Container ile Component bağlantıyı kuracak yapıyı oluşturmaya. connect bu işlemi bizim için gerçekleştiriyoruz

ValueDisp

const ValueDisp1UI = (props) => {
return (
<Square size={50} color="pink" title="C1">
<span style={{ backgroundColor: 'red' }}>{props.counter.value}</span>
</Square>
);
};
export const ValueDisp1 = connect(mapStateToPropsC1, mapDispatchToPropsEmpty)(ValueDisp1UI);

IncButton

const IncBtn1UI = props => {
return (
<button onClick={props.inc}>
Inc1
</button>
);
};

export const IncBtn1 = connect(mapStateToPropsEmpty, mapDispatchToPropsC1)(IncBtn1UI);

Aslında bu yapı diğer yapıdan daha esnek bir yapı oluşturmanıza olanak sağlıyor.

  • ValueDisp1UI ValueDisp2 tanımlarkende aynen kullanabilirim. Bunu Hooklar’da yapamazdık. Çünkü hangi Counter state bileşenin içerisinden söylememiz gerekiyordu.
  • IncBtn1UI IncBtn2 içerisinde aynı şekilde kullanabilirim.

Aynen aşağıdaki gibi. Bu yapı özellikle View ayırdığınız için aynı View Reuse etmek istediğiniz yapılar için oldukça uygun.

export const ValueDisp1 = connect(mapStateToPropsC1, mapDispatchToPropsEmpty)(ValueDisp1UI);
export const ValueDisp2 = connect(mapStateToPropsC2, mapDispatchToPropsEmpty)(ValueDisp1UI);

export const IncBtn1 = connect(mapStateToPropsEmpty, mapDispatchToPropsC1)(IncBtn1UI);
export const IncBtn1 = connect(mapStateToPropsEmpty, mapDispatchToPropsC2)(IncBtn1UI);

RENDERING

Rendering açısından istediğimiz performansta çalıştı ve sadece ilgili bişenin render edilmesini mimari üzerinden gerçekleştirilmesini sağladı.

Sonuç olarak bu yapı ilk başta karmaşık gibi gelsede ..bileşenlerin tekrar reuse edilebilmeleri aradaki cağrımların bileşenden uzaklaştırıldığı için

  • axios
  • fetch
  • graphql
  • scync

istediğiniz yapıyla değiştirmeniz hatta bunları test yapıları ile mocklayabilecek katmanlar oluşturma esnekliği sağlaması açısından Hook kullanımından daha başarılıdır.

Okunabilirlik ve gelecekte React projelerinin Hooks yönelmesi tarafından bakarsak bir dezavantaj oluşturur.

Okumaya Devam Et 😃

Bu yazının devamı veya yazı grubundaki diğer yazılara erişmek için bu linke tıklayabilirsiniz.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store