State Management Asoslari
State management'ning asosiy g'oyasi (Single Source of Truth), Module Pattern + Pub/Sub + Factory'ning createStore ichida birlashishi, setState'ning to'g'ridan-to'g'ri mutatsiyadan nega yaxshiligi (reference equality), Redux'ning reducer pattern'i, va bu yondashuvning V8'dagi GC/tezlik oqibatlari.
80-dars
State Management Asoslari
Kichik dasturda state (holat) — bu shunchaki bir nechta o'zgaruvchi:
let count = 0;
let user = null;
let isLoading = false;
Lekin dastur kattalashganda, muammo paydo bo'ladi: bitta stateга (masalan, user) ko'plab, bir-biridan uzoq joylashgan komponentlar murojaat qiladi va uni o'zgartiradi. Kim, qachon, nima uchun o'zgartirganini kuzatib borish qiyinlashadi:
// Header.js
user.name = 'Yangi ism'; // bu yerda o'zgardi
// Sidebar.js
user.role = 'admin'; // bu yerda ham o'zgardi
// ProfilePage.js
delete user.email; // bu yerda umuman o'chirib yuborildi
Har bir joy user obyektiga to'g'ridan-to'g'ri, nazoratsiz tegishi mumkin. Agar biror joyda bug chiqsa (user.email kutilmaganda undefined), qaysi fayl buni qilgani noma'lum — chunki o'zgartirish huquqi hech qanday markazlashtirilgan nazoratsiz, hamma joyga tarqalib ketgan.
State management — bu muammoni hal qilish uchun state'ni bitta markazga yig'ish va unga faqat belgilangan yo'l orqali (funksiya chaqiruvi orqali, to'g'ridan-to'g'ri emas) tegish qoidasini o'rnatish.
1. Asosiy g'oya — bitta manba (Single Source of Truth)
function createStore(initialState) {
let state = initialState;
const listeners = []; // Pub/Sub'dagi "subscribers" — tanish, shunday emasmi?
function getState() {
return state;
}
function setState(newState) {
state = { ...state, ...newState }; // Shallow clone!
listeners.forEach(listener => listener(state)); // hammaga xabar
}
function subscribe(listener) {
listeners.push(listener);
return () => { // unsubscribe funksiyasini qaytarish - qulay pattern
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
};
}
return { getState, setState, subscribe };
}
const store = createStore({ user: null, isLoading: false });
// Har qanday komponent faqat SHU orqali o'qiydi/yozadi
store.subscribe((state) => console.log('Yangi state:', state));
store.setState({ isLoading: true });
// Yangi state: { user: null, isLoading: true }
Diqqat qiling — bu tuzilma tanish tuyulishi kerak: bu aynan Module Pattern (closure orqali state va listenersni private saqlash) + Pub/Sub (subscribe/notify mexanizmi) + Factory (createStore — store yaratuvchi funksiya) — uchala oldingi mavzuning birlashmasi. State management kutubxonalari (Redux, Zustand) — tub mohiyatan aynan shu g'oyaning kengaytirilgan, qat'iyroq qoidali versiyasi.
2. Nega to'g'ridan-to'g'ri o'zgartirish emas, setState orqali — bu qat'iy qoida
// ❌ YOMON - to'g'ridan-to'g'ri mutatsiya
state.user = newUser;
// ✅ YAXSHI - setState orqali
setState({ user: newUser });
Sabab faqat "tartib" emas — bu yerda texnik sabab ham bor:
- Listener'lar xabardor bo'ladi — agar siz
state.user = newUserdeb to'g'ridan-to'g'ri yozsangiz, hech kim (hech qanday UI) bu o'zgarishni bilmaydi, chunkilisteners.forEach(...)chaqirilmagan. UI yangilanmay qoladi. - Referens taqqoslash (
Object.is/===) ishlab qoladi — bu React kabi kutubxonalarda muhim optimallashtirish bilan bog'liq:
const oldState = { count: 0 };
oldState.count = 1; // to'g'ridan-to'g'ri o'zgartirildi
// oldState === oldState → true (bu SHU BIR XIL obyekt, garchi ichi o'zgargan bo'lsa ham!)
const oldState = { count: 0 };
const newState = { ...oldState, count: 1 }; // yangi obyekt yaratildi (immutability!)
// oldState === newState → false (ikkita TURLI obyekt)
React (va boshqa framework'lar) ichki tekshiruvda oldState === newStateni solishtiradi (bu — reference equality, oddiy pointer taqqoslash, juda tez operatsiya). Agar siz state'ni to'g'ridan-to'g'ri mutatsiya qilsangiz, oldState va newState bir xil obyektga ishora qiladi (=== natijasi true), va React "hech narsa o'zgarmagan" deb noto'g'ri xulosa chiqarib, qayta render qilmaydi — bu klassik "state o'zgardi lekin UI yangilanmadi" bugining sababi.
Bu — avvalda o'rgangan Immutability mavzusining aynan shu yerdagi amaliy zaruriyati: yangi state — har doim yangi obyekt ({ ...oldState, ...changes }) bo'lishi kerak, aynan shu shallow copy orqali === taqqoslash tez va ishonchli ishlashi uchun.
3. Reducer pattern — Redux'ning asosiy g'oyasi
Yuqoridagi setState — istalgan narsani to'g'ridan-to'g'ri o'zgartirish imkonini beradi, bu hali ham nazoratsiz. Redux bir qadam oldinga o'tib, faqat maxsus "action" obyektlar orqali o'zgarishga ruxsat beradi:
function reducer(state, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 };
case 'SET_USER':
return { ...state, user: action.payload };
default:
return state; // noma'lum action - state O'ZGARMASDAN qaytariladi
}
}
function createStore(reducer, initialState) {
let state = initialState;
const listeners = [];
function dispatch(action) {
state = reducer(state, action); // YAGONA joy - state shu yerda o'zgaradi
listeners.forEach(fn => fn(state));
}
function getState() { return state; }
function subscribe(fn) { listeners.push(fn); }
return { dispatch, getState, subscribe };
}
const store = createStore(reducer, { count: 0, user: null });
store.dispatch({ type: 'INCREMENT' });
console.log(store.getState()); // { count: 1, user: null }
reducer — bu pure function: bir xil state + action kiritilsa, har doim bir xil natija qaytaradi, va hech qanday tashqi narsani o'zgartirmaydi (side-effect'siz). Bu xususiyat tufayli:
- Debug qilish oson — har bir o'zgarish
action.typeorqali loglanishi mumkin ("kim, qachon, nima uchun o'zgartirdi" degan savolga aniq javob). - Time-travel debugging — har bir action'ni tartib bilan saqlab, orqaga/oldinga qayta o'ynash mumkin (Redux DevTools aynan shunday ishlaydi) — bu faqat reducer pure bo'lgani uchun mumkin.
4. V8/runtime darajasida — bu nimaga bog'liq
Bu mavzu ham (Design Patterns kabi) V8'ning maxsus mexanizmi emas — bu arxitektura qarori, oddiy closure, obyekt spread, va funksiya chaqiruvlari ustida qurilgan. Lekin ikkita amaliy bog'liqlik bor:
- Shallow copy narxi —
{ ...state, count: 1 }har safar yangi obyekt yaratadi. Katta, chuqur nested state uchun bu har birdispatchda yangi obyekt yaratish degani — agar state juda katta bo'lsa (masalan, minglab elementli massiv), bu xotira va GC bosimini oshirishi mumkin (Garbage Collection — ko'p vaqtinchalik obyekt yaratilishi Minor GC'ni tezroq ishga tushiradi, chunki Young Generation tezroq to'ladi). - Reference equality tezligi — yuqorida aytganimizdek,
oldState === newState— bu pointer taqqoslash, O(1) operatsiya, obyekt ichidagi hamma property'ni solishtirishdan (deep equality, O(n)) ancha tezroq. Shuning uchun immutable pattern ataylab shunday qurilgan — tezlik uchun chuqur emas, sayoz (shallow) taqqoslash yetarli bo'lishi uchun.
Real kutubxonalar — bir xil g'oyaning turli ko'rinishlari
| Kutubxona | Yondashuv |
|---|---|
| Redux | Reducer + Action + bitta global store (yuqoridagi misolning to'liq versiyasi) |
| Zustand | Xuddi shunga o'xshash, lekin kamroq boilerplate, createStorega yaqinroq |
| MobX | Boshqacha falsafa — mutatsiyaga ruxsat beradi, lekin Proxy orqali kuzatadi (qaysi property o'qilgan/yozilgan — avtomatik "reactive" qiladi) |
| React Context + useState | Kichik loyihalar uchun — o'ziga xos mini-store, lekin katta loyihada re-render muammolari chiqishi mumkin |