WisarWisar
Dasturlash kitobi/12-QISM — State Management39 daqiqa

12.2-bob: Redux Toolkit (store, slice, async thunk)

12-QISM — State Management va Data Fetching · 2-mavzu


1. Kirish va motivatsiya

12.1-bobda Context'ni chuqur o'rgandik va uning cheklovlarini ko'rdik: selektor yo'q (qism tinglab bo'lmaydi), katta/tez o'zgaradigan state'da ko'p re-render, va DevTools/debugging imkoniyatlari cheklangan. Aynan shu cheklovlar paydo bo'lganda — ya'ni katta, murakkab ilovada (ko'p bog'liq state, murakkab yangilanishlar, kuchli debugging ehtiyoji, jamoaviy ish) — Redux keladi. Redux — frontend state management'ning eng mashhur, eng ko'p ishlatiladigan va eng "bashoratli" yechimi: butun ilova holatini bitta markaziy joyda (store), qat'iy qoidalar bilan boshqaradi.

Lekin eski Redux'ning yomon nomi bor edi: u juda ko'p boilerplate (takroriy, zerikarli kod) talab qilardi — har action uchun konstanta, action creator, reducer, switch... oddiy "hisoblagich"ni yozish uchun 5 fayl kerak bo'lardi. Aynan shu sababli ko'p dasturchi Redux'dan qochardi. Redux Toolkit (RTK) — Redux jamoasining o'zi yaratgan rasmiy, zamonaviy yechim: u boilerplate'ni keskin kamaytiradi (createSlice — reducer va action'ni avtomatik), Immerni o'z ichiga oladi (state'ni "mutatsiya qilgandek" yozasiz, lekin natija immutable), va configureStore (DevTools, middleware avtomatik sozlab keladi). Bugun "Redux yoz" degani "Redux Toolkit yoz" degani (eski qo'lda Redux endi yozilmaydi — rasman tavsiya etilmaydi).

Bu bob: nega global state, nega Redux (Context cheklovi — 12.1), Redux asosiy tushunchalari (store, action, reducer, dispatch — Flux/bir tomonlama oqim), nega RTK (boilerplate muammosi), configureStore (store sozlash), createSlice (reducer + action avtomatik), Immer (mutatsiya sintaksisi — immutable natija), useSelector/useDispatch (tabiiy selektor — Context cheklovini hal qiladi), createAsyncThunk (async — loading/error holatlari), Redux DevTools (vaqt sayohati — debugging), createEntityAdapter (normalizatsiya), memoized selektorlar (createSelector), TypeScript bilan RTK (typed hooks), va qachon Redux (Context/Zustand bilan taqqos) — mavzuni to'liq, zamonaviy (RTK) va amaliy holatda ochamiz.

O'xshatish: Redux — bu davlatning markaziy bank va qonun tizimi. Context 12.1-bob — bu mahallada pul almashish: oddiy, tez, lekin katta miqyosda tartibsiz bo'ladi (kim qancha, qachon o'zgartirdi — kuzatib bo'lmaydi). Redux — bu markaziy bank (store — barcha pul/holat bitta joyda) + qonun (reducer — pulni faqat qonuniy yo'l bilan, action orqali o'zgartirish mumkin) + rasmiy yozuvlar (har tranzaksiya — action — qaydlanadi, DevTools'da ko'rinadi). Hech kim pulni to'g'ridan o'zgartira olmaydi — faqat rasmiy so'rov (action) yuboradi, bank (reducer) qoidaga qarab bajaradi. Bu — biroz ko'proq rasmiyatchilik, lekin bashoratli, kuzatiladigan, ishonchli (katta tizim uchun zarur). Redux Toolkit — bu shu bankning zamonaviy, raqamli versiyasi: ilgari har tranzaksiya uchun 5 ta qog'oz to'ldirilardi (boilerplate), endi bir tugma (createSlice).

Nega muhim?

  • Katta ilova standarti — murakkab, ko'p holatli ilovada Redux — eng keng yechim (jamoaviy, bashoratli).
  • Bashoratlilik + debugging — DevTools (vaqt sayohati), qat'iy oqim — xato topishni osonlashtiradi.
  • Stack'da bor — Redux Toolkit va RTK Query zamonaviy React stack'ining muhim qismi (ko'p loyihada uchraydi).
  • Ish bozori — ko'p kompaniya Redux ishlatadi (legacy va yangi) — bilish majburiy.

2. Nazariya — chuqur tushuntirish

2.1. Nega global state, nega Redux

text
  CONTEXT YETMAYDI QACHON (12.1: 2.9, 2.10):
   Katta, murakkab state (ko'p bog'liq qism)
   Tez-tez o'zgaradigan (selektor yo'q  ko'p re-render — 12.1: 2.3)
   Murakkab yangilanishlar (ko'p joydan bir state'ni o'zgartirish)
   Kuchli debugging (vaqt sayohati, har o'zgarishni kuzatish)
   Jamoaviy (qat'iy struktura — har kim bir xil naqshda)

  REDUX BERADIGAN NARSALAR:
   Markaziy store (butun holat bitta joyda — "single source of truth")
   Bashoratli oqim (faqat action orqali o'zgartirish — qat'iy)
   Tabiiy selektor (useSelector — faqat kerakli qism tinglash — 2.7)
   DevTools (vaqt sayohati, har action ko'rinadi — 2.9)
   Middleware (async, logging, custom mantiq — 2.8)

   Context — kichik/oddiy global state; Redux — KATTA, MURAKKAB, kuzatiladigan state
   Redux — "boilerplate ko'p" edi  Redux Toolkit buni HAL QILDI (2.4)

Nega global state, nega Redux — Redux'ning o'rnini tushunish. Context qachon yetmaydi (12.1: 2.9, 2.10): katta, murakkab state (ko'p bog'liq qism); tez-tez o'zgaradigan (selektor yo'q — ko'p re-render — 12.1: 2.3); murakkab yangilanishlar (ko'p joydan bir state'ni o'zgartirish); kuchli debugging ehtiyoji (vaqt sayohati); jamoaviy ish (qat'iy struktura). Redux beradigan narsalar: (1) markaziy store (butun holat bitta joyda — "single source of truth"); (2) bashoratli oqim (state'ni faqat action orqali o'zgartirish — qat'iy, kuzatiladigan); (3) tabiiy selektor (useSelector — faqat kerakli qism tinglash — Context'ning asosiy cheklovini hal qiladi — 2.7); (4) DevTools (vaqt sayohati — har action va state o'zgarishini ko'rish, hatto "orqaga qaytarish" — 2.9); (5) middleware (async, logging, custom mantiq — 2.8). Ikki nuqta: (1) Context — kichik/oddiy global state uchun (theme, auth), Redux — katta, murakkab, kuzatiladigan state uchun; (2) eski Redux'ning asosiy kamchiligi "boilerplate ko'p" edi — Redux Toolkit 2.4-bob buni hal qildi (shuning uchun bugun Redux = RTK). Bu — Redux'ni qachon ishlatish qarorining asosi.

2.2. Redux asosiy tushunchalari — Flux va bir tomonlama oqim

text
  REDUX UCH TUSHUNCHA (Flux arxitekturasi — bir tomonlama oqim):

  ┌─────────────────────────────────────────────────────────────┐
  │  1. STORE — butun holat (state) BITTA obyektda (single source)│
  │     { user: {...}, cart: [...], theme: "dark" }              │
  │                                                                │
  │  2. ACTION — "nima bo'ldi" (obyekt — type + payload):         │
  │     { type: "cart/add", payload: product }                    │
  │                                                                │
  │  3. REDUCER — eski state + action  yangi state (sof funksiya):│
  │     (state, action) => newState                               │
  └─────────────────────────────────────────────────────────────┘

  BIR TOMONLAMA OQIM (predictable — bashoratli):
  Komponent ──dispatch(action)──► Store (reducer ishlaydi) ──► yangi state ──► komponent render
       ▲                                                                            │
       └────────────────────── useSelector (o'qiydi) ◄─────────────────────────────┘

   State'ni FAQAT action orqali o'zgartirish (to'g'ridan emas — qat'iy oqim)
   Reducer SOF (11.6: 2.3) — bir xil action  bir xil natija (bashoratli, sinaladigan)

Redux asosiy tushunchalari — uch ustun (Flux arxitekturasi — bir tomonlama ma'lumot oqimi). (1) Store — butun ilova holati (state) bitta obyektda ({ user, cart, theme } — "single source of truth"). (2) Action — "nima bo'ldi"ni tasvirlovchi obyekt ({ type: "cart/add", payload: product }type va payload — 11.6: 2.4 dagi useReducer action bilan bir xil g'oya). (3) Reducer — eski state va action olib, yangi state qaytaruvchi sof funksiya ((state, action) => newState — 11.6: 2.3). Bir tomonlama oqim (bashoratlilikning kaliti): komponent dispatch(action) chaqiradi store'da reducer ishlaydi yangi state komponent re-render (useSelector orqali o'qiydi). Ma'lumot doim bir yo'nalishda aylanadi (komponent action store komponent). Ikki tamoyil: (1) state'ni faqat action orqali o'zgartirish mumkin (to'g'ridan emas — bu qat'iylik bashoratlilik beradi: har o'zgarish kuzatiladi); (2) reducer sof bo'lishi shart (11.6: 2.3 — side effect yo'q, immutable) — shuning uchun bir xil action har doim bir xil natija beradi (bashoratli, sinaladigan, DevTools'da qaytariladigan). Bu uch tushuncha — Redux'ning falsafasi (RTK ularni soddalashtiradi, lekin asos bir xil).

2.3. Redux store qatlamlari (umumiy ko'rinish)

text
  REDUX QISMLARI (RTK bilan — qanday joylashadi):

  store.ts ──────────── configureStore({ reducer: { cart, auth, ... } })
     │                  (barcha slice'larni birlashtiradi + DevTools/middleware)
     ▼
  features/cart/cartSlice.ts ── createSlice({ name, initialState, reducers })
     │                          (cart holati + uni o'zgartiruvchi reducer'lar)
     ▼
  Komponent ──── useSelector(s => s.cart)  +  useDispatch()
                 (o'qish)                       (yozish — dispatch(action))

  PROVIDER (Context kabi — store'ni ilovaga beradi):
  <Provider store={store}>   // react-redux Provider (ildizda)
    <App />
  </Provider>

  FAYL TUZILISHI (feature-based — 11.3: 2.9):
  src/
  ├── app/store.ts              configureStore
  └── features/
      ├── cart/cartSlice.ts     cart slice
      └── auth/authSlice.ts     auth slice

   store (markaz) + slice'lar (har feature holati) + Provider (ilovaga ulashadi)
   Feature-based — har slice o'z papkasida (cart/, auth/ — 11.3: 2.9)

Redux store qatlamlari — RTK bilan Redux qanday joylashishini ko'rsatadi. Asosiy qismlar: (1) store.tsconfigureStore({ reducer: { cart, auth } }) (barcha slice'larni birlashtiradi va DevTools/middleware'ni avtomatik sozlaydi — 2.4); (2) slice fayllari (features/cart/cartSlice.ts) — createSlice bilan har feature'ning holati va uni o'zgartiruvchi reducer'lari; (3) komponentuseSelector(s => s.cart) (o'qish) va useDispatch() (yozish — 2.7). Provider (react-redux) — store'ni butun ilovaga beradi: <Provider store={store}><App /></Provider> (ildizda, Context Provider kabi). Fayl tuzilishi — feature-based (11.3: 2.9): app/store.ts (markaz), features/cart/cartSlice.ts, features/auth/authSlice.ts (har feature o'z papkasida). Ikki nuqta: (1) Redux uch qismdan iborat — store (markaz), slice'lar (har feature holati), Provider (ilovaga ulashadi); (2) feature-based tuzilish — har slice o'z papkasida (cart, auth — kengaytiriladigan, tartibli). Bu — Redux ilovasining umumiy arxitekturasi (har qismni keyingi bo'limlarda batafsil ko'ramiz).

2.4. configureStore va createSlice — RTK asoslari

text
  createSlice — reducer va action'ni AVTOMATIK (eski Redux boilerplate'ini yo'qotadi):

  import { createSlice } from "@reduxjs/toolkit";

  const counterSlice = createSlice({
    name: "counter",                    // slice nomi (action type prefiksi: "counter/...")
    initialState: { value: 0 },         // boshlang'ich holat
    reducers: {                          // o'zgartiruvchi funksiyalar:
      increment: (state) => { state.value += 1; },   //  "mutatsiya" (Immer — 2.5)
      decrement: (state) => { state.value -= 1; },
      addBy: (state, action) => { state.value += action.payload; },   // payload bilan
    },
  });

  export const { increment, decrement, addBy } = counterSlice.actions;   //  action'lar AVTOMATIK
  export default counterSlice.reducer;                                    // reducer

  configureStore — store yaratadi (DevTools/middleware AVTOMATIK):
  const store = configureStore({
    reducer: { counter: counterSlice.reducer },   // slice'larni birlashtir
  });

   createSlice — name + initialState + reducers  action va reducer AVTOMATIK (boilerplate yo'q)
   configureStore — DevTools, thunk middleware, immutability tekshiruvi AVTOMATIK

configureStore va createSlice — RTK'ning ikki asosiy vositasi (eski Redux boilerplate'ini yo'qotadi). createSlice — bir joyda slice nomi, boshlang'ich holat, va reducer'larni belgilaydi, va undan action'larni avtomatik generatsiya qiladi: createSlice({ name: "counter", initialState: { value: 0 }, reducers: { increment: (state) => { state.value += 1 } } })name (action type prefiksi — "counter/increment"), initialState, va reducers (o'zgartiruvchi funksiyalar). Diqqat: reducer ichida state.value += 1 — "mutatsiya"dek ko'rinadi, lekin bu Immer tufayli xavfsiz 2.5-bob. createSlicedan action creator'lar (increment, decrement) va reducer avtomatik chiqadi (counterSlice.actions, counterSlice.reducer) — eski Redux'da bularning har birini qo'lda yozish kerak edi. configureStore — store yaratadi va DevTools, thunk middleware (async uchun), immutability/serializability tekshiruvini avtomatik sozlaydi (eski Redux'da bularning hammasini qo'lda ulash kerak edi). Ikki nuqta: (1) createSlice — name + initialState + reducers'dan action va reducer'ni avtomatik chiqarads (boilerplate'ni yo'qotadi — RTK'ning eng katta yutug'i); (2) configureStore — best-practice middleware va DevTools'ni avtomatik beradi. Bu ikkisi — zamonaviy Redux'ning asosi (eski qo'lda Redux endi yozilmaydi).

2.5. Immer — mutatsiya sintaksisi, immutable natija

text
  IMMER — RTK ICHIDA (state'ni "mutatsiya qilgandek" yoziladi, natija IMMUTABLE):

  // Eski Redux (qo'lda immutable — zerikarli — 11.4: 2.7):
  case "add": return { ...state, items: [...state.items, action.payload] };
  case "update": return { ...state, user: { ...state.user, name: action.payload } };

  // RTK + Immer (mutatsiya sintaksisi — Immer immutable qiladi):
  reducers: {
    add: (state, action) => { state.items.push(action.payload); },        // push — Immer OK
    update: (state, action) => { state.user.name = action.payload; },     // to'g'ridan — OK
  }

  QANDAY ISHLAYDI (ichki):
  - Immer "draft" (qoralama) yaratadi — unga mutatsiya qilinadi
  - Immer o'zgarishlarni kuzatib, YANGI immutable obyekt hosil qiladi (eski tegmaydi)
   "mutatsiya" yoziladi, lekin natija immutable (ikkalasining foydasi)

   FAQAT createSlice/createReducer ICHIDA mutatsiya mumkin (Immer o'rab turibdi)
   Tashqarida (oddiy funksiya) mutatsiya — XATO (Immer yo'q — immutability buziladi — 11.4: 2.7)

Immer — RTK'ning ichidagi kutubxona, u state'ni "mutatsiya qilgandek" yozishga imkon beradi, lekin natija immutable bo'ladi. Eski Redux'da immutability'ni qo'lda saqlash kerak edi ({ ...state, items: [...state.items, payload] } — zerikarli, xatoga moyil, ayniqsa ichma-ich obyektlarda — 11.4: 2.7, 2.8). RTK + Immer bilan esa to'g'ridan mutatsiya sintaksisi ishlatasiz: state.items.push(payload), state.user.name = payload (push, to'g'ridan tayinlash — odatda taqiqlangan, lekin bu yerda xavfsiz). Qanday ishlaydi (ichki mexanizm): Immer "draft" (qoralama nusxa) yaratadi, siz unga mutatsiya qilasiz, Immer o'zgarishlarni kuzatib yangi immutable obyekt hosil qiladi (eski state tegmaydi) — natijada siz mutatsiyaning qulayligini (oddiy yozuv) va immutability'ning foydasini (bashoratlilik, performance) ikkalasini olasiz. Eng muhim ogohlantirish: mutatsiya sintaksisi faqat createSlice/createReducer ichida ishlaydi (Immer o'rab turibdi); tashqarida (oddiy funksiya, useState'da) mutatsiya — xato (Immer yo'q, immutability buziladi — 11.4: 2.7). Bu farqni anglash muhim — RTK ichida mutatsiya OK, tashqarida taqiq. Immer — RTK'ni yozishda eng yoqimli qilgan xususiyat.

2.6. useSelector va useDispatch — komponent bilan ulanish

text
  react-redux hooklari (store'ni komponentga ulaydi):

  // useSelector — store'dan KERAKLI qismni O'QISH (tabiiy selektor — 12.1: 2.9 yechimi):
  const count = useSelector((state) => state.counter.value);   // faqat counter.value
  const items = useSelector((state) => state.cart.items);

  // useDispatch — action YUBORISH (yozish):
  const dispatch = useDispatch();
  dispatch(increment());                  // action creator (createSlice'dan — 2.4)
  dispatch(addBy(5));                      // payload bilan

  SELEKTOR KUCHI (Context'dan farq — 12.1: 2.3):
   useSelector(s => s.counter.value) — FAQAT counter.value o'zgarganda komponent render
   cart yoki auth o'zgarsa — BU komponent render BO'LMAYDI (faqat tinglagan qism)

  ┌────────────────────────────────────────────────────────────┐
  │ useSelector: store'dan qism o'qish (faqat o'sha qism o'zgarsa │
  │ render) | useDispatch: action yuborish (o'zgartirish)         │
  └────────────────────────────────────────────────────────────┘

   useSelector — TABIIY selektor (faqat tinglagan qism o'zgarsa render — Context'ning yechimi)
   useSelector qiymat REFERENTIAL teng bo'lsa render yo'q (obyekt qaytarsa — memoize — 2.10)

useSelector va useDispatch — react-redux hooklari, store'ni komponentga ulaydi. useSelector(selektor) — store'dan kerakli qismni o'qiydi: const count = useSelector((state) => state.counter.value) — bu tabiiy selektor (12.1: 2.9 dagi Context cheklovining yechimi): komponent faqat o'zi tinglagan qism (counter.value) o'zgarganda render bo'ladi; cart yoki auth o'zgarsa — bu komponent render bo'lmaydi. Bu — Redux'ning Context'dan eng katta afzalligi: fine-grained reactivity (faqat kerakli qismga reaksiya). useDispatch() — action yuboruvchi dispatch funksiyasini qaytaradi: dispatch(increment()), dispatch(addBy(5)) (action creator'lar — createSlice'dan, payload bilan). Ikki nuqta: (1) useSelector — tabiiy selektor (faqat tinglagan qism o'zgarganda render — Context'ning asosiy muammosini hal qiladi); (2) useSelector qaytargan qiymat referential teng bo'lsa (===) render bo'lmaydi — lekin agar selektor yangi obyekt qaytarsa (s => ({ a: s.a })) har safar yangi havola har render re-render (bu yerda memoized selektor kerak — createSelector — 2.10). Bu hooklar — Redux'ni komponent bilan bog'lashning standart usuli.

2.7. createAsyncThunk — async (loading/error)

text
  createAsyncThunk — ASYNC amal (API so'rovi) + loading/error AVTOMATIK:

  import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";

  // 1. Async thunk (API so'rovi):
  export const fetchUsers = createAsyncThunk("users/fetch", async () => {
    const res = await fetch("/api/users");
    return res.json();              // bu — fulfilled payload bo'ladi
  });

  // 2. Slice — thunk'ning 3 holatini boshqaradi (extraReducers):
  const usersSlice = createSlice({
    name: "users",
    initialState: { items: [], loading: false, error: null },
    reducers: {},
    extraReducers: (builder) => {
      builder
        .addCase(fetchUsers.pending, (state) => { state.loading = true; })       // boshlandi
        .addCase(fetchUsers.fulfilled, (state, action) => {                      // muvaffaqiyat
          state.loading = false; state.items = action.payload;
        })
        .addCase(fetchUsers.rejected, (state, action) => {                       // xato
          state.loading = false; state.error = action.error.message;
        });
    },
  });

  // Komponentda: dispatch(fetchUsers());   pending  fulfilled/rejected (avtomatik)

   createAsyncThunk — async + 3 holat (pending/fulfilled/rejected) AVTOMATIK
   extraReducers — thunk holatlarini boshqaradi (loading/error qo'lda — lekin tartibli)

createAsyncThunk — async amallar (API so'rovi) va ularning loading/error holatlarini boshqaradi. Eski Redux'da async — murakkab edi (qo'lda dispatch ketma-ket, loading flag'lar). RTK soddalashtiradi: (1) thunk yaratishcreateAsyncThunk("users/fetch", async () => { const res = await fetch(...); return res.json() }) — birinchi argument action type prefiksi, ikkinchi async funksiya (qaytargan qiymat — fulfilled payload). (2) slice'da 3 holatni boshqarishextraReducers (builder) bilan: pending (so'rov boshlandi — loading = true), fulfilled (muvaffaqiyat — items = payload, loading = false), rejected (xato — error = action.error.message). Komponentda dispatch(fetchUsers()) — RTK avtomatik pending fulfilled/rejected action'larini yuboradi. Ikki nuqta: (1) createAsyncThunk — async + uch holat (pending/fulfilled/rejected) avtomatik (qo'lda loading flag boshqarish kerak emas — RTK qiladi); (2) extraReducers — thunk holatlarini slice'da boshqarish (loading/error qo'lda yoziladi, lekin tartibli va bashoratli). MUHIM: server ma'lumot uchun createAsyncThunk ishlab bo'ladi, lekin ko'p holatda RTK Query 12.3-bob yoki TanStack Query 12.4-bob ancha yaxshiroq (kesh, fonda yangilash, qayta urinish avtomatik — 12.1: 2.10). createAsyncThunk — oddiy async yoki client-side async amallar uchun.

2.8. Middleware va Redux DevTools

text
  MIDDLEWARE — action'ning store'ga yetishidan OLDIN oraliq mantiq (logging, async, custom):

  dispatch(action) ──► [middleware 1] ──► [middleware 2] ──► reducer ──► store
                       (har action shu yerdan o'tadi — logging, async, analytics)

  RTK'da avtomatik middleware (configureStore):
  - redux-thunk (async — createAsyncThunk ishlashi uchun)
  - immutability tekshiruvi (dev — mutatsiyani tutadi)
  - serializability tekshiruvi (dev — non-serializable state'ni tutadi)

  CUSTOM middleware (masalan logging):
  const logger = (store) => (next) => (action) => {
    console.log("dispatch:", action.type);
    return next(action);          // keyingi middleware/reducer'ga uzat
  };

  REDUX DEVTOOLS (brauzer kengaytmasi — Redux'ning super-kuchi):
   Har action va state o'zgarishini ko'rish (kuzatish)
   VAQT SAYOHATI — oldingi holatga qaytarish (debug)
   Action'ni "qayta o'ynatish", state'ni tekshirish

   Middleware — action oqimiga oraliq mantiq (RTK avtomatik thunk/tekshiruv beradi)
   DevTools — Redux'ning eng katta afzalligi (vaqt sayohati — debugging — 2.1)

Middleware va Redux DevTools — Redux'ning ikki kuchli imkoniyati. Middleware — action store'ga (reducer'ga) yetishidan oldin ishlaydigan oraliq mantiq: har dispatch qilingan action middleware zanjiri orqali o'tadi (logging, async, analytics, custom). RTK'da configureStore avtomatik muhim middleware'ni qo'shadi: redux-thunk (async — createAsyncThunk ishlashi uchun), immutability tekshiruvi (development — agar state'ni tashqarida mutatsiya qilsangiz tutadi), serializability tekshiruvi (non-serializable qiymat — masalan Promise, funksiya — state'da bo'lsa ogohlantiradi). Custom middleware ham yozish mumkin ((store) => (next) => (action) => {...} — masalan har action'ni loglash). Redux DevTools (brauzer kengaytmasi) — Redux'ning super-kuchi: har action va state o'zgarishini ko'rish (kuzatish — qaysi action, qanday o'zgartirdi), vaqt sayohati (oldingi holatlarga qaytarish — xatoni topish uchun bebaho), action'ni qayta o'ynatish, state'ni tekshirish. Ikki nuqta: (1) middleware — action oqimiga oraliq mantiq (RTK avtomatik thunk va dev-tekshiruvlarni beradi); (2) DevTools — Redux'ning Context/Zustand'dan eng katta afzalligi (vaqt sayohati bilan murakkab state buglarini topish — 2.1). Bu — Redux'ni "katta, murakkab ilova"da ishlatishning asosiy sabablaridan biri.

2.9. createEntityAdapter — normalizatsiya

text
  NORMALIZATSIYA — ro'yxatni ID bo'yicha OBYEKT sifatida saqlash (tez qidiruv/yangilash):

   Massiv (qidirish/yangilash — O(n) — sekin katta ro'yxatda):
  items: [{ id: "1", name: "A" }, { id: "2", name: "B" }]
  // yangilash: items.map(i => i.id === "1" ? {...i, ...} : i)  — butun massivni aylanish

   Normalizatsiya (ID  obyekt — O(1) qidiruv/yangilash):
  { ids: ["1", "2"], entities: { "1": {name:"A"}, "2": {name:"B"} } }
  // yangilash: entities["1"] = {...}  — to'g'ridan (tez)

  createEntityAdapter — normalizatsiyani AVTOMATIK qiladi:
  const usersAdapter = createEntityAdapter();
  const usersSlice = createSlice({
    name: "users",
    initialState: usersAdapter.getInitialState({ loading: false }),
    reducers: {
      addUser: usersAdapter.addOne,          //  tayyor CRUD reducer'lar
      updateUser: usersAdapter.updateOne,
      removeUser: usersAdapter.removeOne,
    },
  });
  // Selektorlar ham avtomatik: usersAdapter.getSelectors()

   createEntityAdapter — ro'yxatni normalize (IDobyekt) + CRUD reducer/selektor AVTOMATIK
   Katta ro'yxat (foydalanuvchi, mahsulot)da tez qidiruv/yangilash (O(1) — performance)

createEntityAdapter — ro'yxat (kolleksiya) holatini boshqarishning ilg'or, performant usuli. Normalizatsiya — ro'yxatni massiv o'rniga ID bo'yicha obyekt sifatida saqlash. Massivda element qidirish/yangilash O(n) (butun massivni aylanish — items.map(...) — sekin katta ro'yxatda); normalize qilingan shaklda ({ ids: [...], entities: { "1": {...} } }) — qidirish/yangilash O(1) (entities["1"] — to'g'ridan, tez). createEntityAdapter — bu normalizatsiyani avtomatik qiladi: usersAdapter.getInitialState() (normalize qilingan boshlang'ich holat), va tayyor CRUD reducer'lar (addOne, updateOne, removeOne, setAll...) hamda selektorlar (getSelectorsselectAll, selectById). Slice'da: reducers: { addUser: usersAdapter.addOne, updateUser: usersAdapter.updateOne }. Ikki nuqta: (1) createEntityAdapter — ro'yxatni normalize qiladi va CRUD reducer/selektorni avtomatik beradi (qo'lda yozish kerak emas); (2) katta ro'yxat (foydalanuvchilar, mahsulotlar, buyurtmalar)da tez qidiruv/yangilash (O(1) — performance). Bu — Redux'da ro'yxat-holat boshqaruvining professional naqshi (lekin server ro'yxati uchun ko'pincha RTK/TanStack Query afzal — 12.3, 12.4).

2.10. Memoized selektorlar — createSelector

text
  MUAMMO: useSelector YANGI obyekt/hisob qaytarsa — har render re-render (referential — 2.6):

   const filtered = useSelector(s => s.items.filter(i => i.active));   // HAR render yangi massiv!
  //  filter har safar yangi massiv  useSelector "o'zgardi" deb biladi  har render re-render

   createSelector — natijani KESHLAYDI (kirish o'zgarmasa — eski natija):
  import { createSelector } from "@reduxjs/toolkit";

  const selectActiveItems = createSelector(
    (state) => state.items,                  // kirish 1 (input selektor)
    (items) => items.filter((i) => i.active) // hisob (kirish o'zgargandagina qayta hisob)
  );
  const filtered = useSelector(selectActiveItems);   // memoized — kerakli render

  // Argument bilan (parametrli selektor):
  const selectById = createSelector(
    [(state) => state.items, (state, id) => id],
    (items, id) => items.find((i) => i.id === id)
  );

   createSelector — qimmat/hosilangan hisobni keshlaydi (reselect — 11.6: 2.8 g'oyasi)
   useSelector'da YANGI obyekt qaytaradigan selektor  createSelector (aks holda har render re-render)

Memoized selektorlar — createSelectoruseSelectorning performance tuzog'ini hal qiladi. Muammo: agar useSelector yangi obyekt yoki hisoblangan qiymat qaytarsa (s => s.items.filter(i => i.active)filter har safar yangi massiv yaratadi), useSelector uni === bilan solishtirganda "o'zgardi" deb biladi (referential — 2.6) komponent har render re-render bo'ladi (garchi ma'lumot o'zgarmagan bo'lsa ham). YechimcreateSelector (RTK'ning reselect'i) — natijani keshlaydi: createSelector((state) => state.items, (items) => items.filter(i => i.active)) — birinchi (input) selektor kirishni oladi, ikkinchi funksiya hisoblaydi (faqat kirish o'zgarganda qayta hisoblab, aks holda oldingi natijani qaytaradi). const filtered = useSelector(selectActiveItems) — endi memoized (faqat items o'zgarganda yangi). Parametrli selektor ham mumkin (id bilan qidirish). Ikki nuqta: (1) createSelector — qimmat yoki hosilangan (filter/sort/hisob) qiymatni keshlaydi (useMemoning Redux selektor versiyasi — 11.6: 2.8 g'oyasi); (2) useSelectorda yangi obyekt/massiv qaytaradigan selektor uchun createSelector majburiy (aks holda har render re-render — eng keng Redux performance tuzog'i). Bu — Redux'ni performant ishlatishning zarur naqshi.

2.11. TypeScript bilan RTK va qachon Redux

text
  TYPED HOOKS (RTK + TypeScript — 11.14):
  // store turlarini chiqarish:
  export type RootState = ReturnType<typeof store.getState>;
  export type AppDispatch = typeof store.dispatch;

  // Typed hooklar (har joyda qayta yozmaslik):
  export const useAppDispatch = () => useDispatch<AppDispatch>();
  export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

  // Komponentda — to'liq type-safe:
  const count = useAppSelector((state) => state.counter.value);   // state: RootState (autocomplete)

  QACHON REDUX (Context/Zustand bilan taqqos):
  ┌──────────────┬─────────────────────────────────────────────┐
  │ Context      │ kam o'zgaradigan global (theme/auth — 12.1)   │
  │ Zustand      │ oddiy global state (yengil, kam boilerplate)  │
  │ REDUX (RTK)  │ KATTA, murakkab, jamoaviy, DevTools/middleware│
  │ RTK/TanStack │ SERVER ma'lumoti (12.3/12.4 — Redux EMAS)    │
  └──────────────┴─────────────────────────────────────────────┘

   RTK + TypeScript — typed hooks (useAppSelector/useAppDispatch — type-safe)
   Redux — katta/murakkab/jamoaviy; kichik  Zustand/Context; server  Query

TypeScript bilan RTK va qachon Redux — yakuniy ikki mavzu. Typed hooks (RTK + TypeScript — 11.14): store turlarini chiqarasiz (type RootState = ReturnType<typeof store.getState>, type AppDispatch = typeof store.dispatch), va typed hooklar yaratasiz (useAppDispatch, useAppSelector) — har joyda turni qayta yozmaslik uchun. Komponentda useAppSelector((state) => state.counter.value)state to'liq turlangan (autocomplete, type-safe). Qachon Redux (eng muhim qaror — Context/Zustand bilan taqqos): Context — kam o'zgaradigan global (theme/auth — 12.1); Zustand — oddiy global state (yengil, kam kod — 12.5); Redux (RTK)katta, murakkab, jamoaviy ilova (ko'p bog'liq state, DevTools/middleware ehtiyoji, qat'iy struktura); RTK Query/TanStack Queryserver ma'lumoti (12.3/12.4 — Redux state'da emas). Ikki yakuniy nuqta: (1) RTK + TypeScript — typed hooks (useAppSelector/useAppDispatch) bilan to'liq type-safe; (2) Redux — katta/murakkab/jamoaviy ilova uchun (kichik Zustand/Context — kam boilerplate; server ma'lumoti Query). Eslatma: zamonaviy tendensiya — client state kam (ko'p ma'lumot server'dan — Query bilan), shuning uchun Redux ilgaridan kamroq ishlatiladi (lekin katta ilovalarda hali ham standart). To'g'ri vositani tanlash — tajribali dasturchining muhim ko'nikmasi.

2.12. Prepare callback, shallowEqual va listener middleware

text
  PREPARE CALLBACK — action payload'ini reducer'dan OLDIN tayyorlash (id/timestamp):

  reducers: {
    addTodo: {
      reducer: (state, action) => { state.push(action.payload); },  // sof reducer
      prepare: (text) => ({ payload: { id: nanoid(), text, done: false } }),  // payload tayyorlash
    },
  }
  // dispatch(addTodo("Non olish"))   prepare id/done qo'shadi  reducer'ga tayyor payload

  SHALLOWEQUAL — useSelector obyekt qaytarganda ortiqcha render'ni to'xtatish:

  import { shallowEqual, useSelector } from "react-redux";
  //  const { a, b } = useSelector(s => ({ a: s.a, b: s.b }));  // har safar yangi obyekt  render
  const { a, b } = useSelector(s => ({ a: s.a, b: s.b }), shallowEqual);  // sayoz solishtir  render yo'q
  // yoki: ikki alohida useSelector (har biri primitiv — yaxshiroq)

  LISTENER MIDDLEWARE — action'ga "reaksiya" (side effect — thunk'ning yengil muqobili):

  import { createListenerMiddleware } from "@reduxjs/toolkit";
  const listener = createListenerMiddleware();
  listener.startListening({
    actionCreator: logout,                          // logout dispatch bo'lganda...
    effect: async (action, api) => {                // ...bu effekt ishlaydi
      localStorage.removeItem("token");             // side effect (masalan tozalash)
      api.dispatch(cartActions.clearCart());        // boshqa action ham chaqirish mumkin
    },
  });
  // store'ga: middleware: (gd) => gd().prepend(listener.middleware)

   prepare — payload'ni tayyorlash (id/timestamp — reducer sof qoladi)
   shallowEqual — obyekt qaytaruvchi useSelector'da ortiqcha render'ni to'xtatadi
   listener middleware — action'ga reaksiya (side effect) — thunk'ning zamonaviy muqobili

Prepare callback, shallowEqual va listener middleware — RTK'ning uch qo'shimcha, lekin amalda tez-tez kerak bo'ladigan imkoniyati. (1) Prepare callback — action payload'ini reducer ishlashidan oldin tayyorlash usuli. Ko'pincha payload'ga id (masalan nanoid()), timestamp yoki boshqa hosilaviy maydonlar qo'shish kerak bo'ladi — buni reducer ichida qilish reducer'ni nosof qilardi (Date.now(), tasodifiy id — side effect). Yechim: reducers: { addTodo: { reducer, prepare } }prepare payload'ni shakllantiradi ((text) => ({ payload: { id: nanoid(), text, done: false } })), reducer esa tayyor payload'ni sof tarzda qo'llaydi. Chaqirish o'zgarmaydi: dispatch(addTodo("Non olish")). (2) shallowEqualuseSelector yangi obyekt qaytarganda (s => ({ a, b })) ortiqcha render muammosini hal qilishning yana bir yo'li (2.6, 2.10 bilan bir oilada): useSelector ikkinchi argument sifatida solishtirish funksiyasini qabul qiladi; shallowEqual obyektni sayoz (bir daraja) solishtiradi — maydonlar bir xil bo'lsa render bo'lmaydi. Muqobil (odatda afzal): bir nechta alohida useSelector (har biri primitiv qaytaradi). (3) Listener middleware (createListenerMiddleware) — muayyan action'ga reaksiya qilish (side effect) uchun rasmiy vosita — redux-saga/redux-observablening yengil, rasmiy muqobili. startListening({ actionCreator, effect }) — masalan logout dispatch bo'lganda localStorage'ni tozalash yoki boshqa action chaqirish. createAsyncThunkdan farqi: thunk — bitta amalni boshlash, listener — action'lar oqimiga reaksiya (debounce, "shu action'dan keyin buni bajar" mantiqi). Uch nuqta: (1) prepare — payload'ni tayyorlash (reducer sof qoladi); (2) shallowEqual — obyekt qaytaruvchi selektorda ortiqcha render'ni to'xtatadi; (3) listener middleware — action'ga reaksiya (thunk'ning zamonaviy muqobili). Bular — RTK'ni professional darajada ishlatishning qo'shimcha vositalari.

2.13. redux-persist va test

text
  REDUX-PERSIST — store'ni localStorage'ga SAQLASH (sahifa yangilanganda holat qoladi):

  import { persistStore, persistReducer } from "redux-persist";
  import storage from "redux-persist/lib/storage";   // localStorage

  const persistConfig = { key: "root", storage, whitelist: ["cart", "auth"] };  // qaysi slice
  const persistedReducer = persistReducer(persistConfig, rootReducer);

  const store = configureStore({
    reducer: persistedReducer,
    middleware: (gd) => gd({
      serializableCheck: { ignoredActions: ["persist/PERSIST", "persist/REHYDRATE"] },
    }),
  });
  export const persistor = persistStore(store);
  // <PersistGate loading={null} persistor={persistor}><App/></PersistGate>

  TEST — slice va thunk (reducer sof  test oson):

  // slice reducer testi (Immer natijasini tekshirish):
  expect(counterReducer({ value: 0 }, increment())).toEqual({ value: 1 });

  // thunk testi (fetch mock, holatlarni tekshirish):
  const state = usersReducer(initial, fetchUsers.fulfilled([{ id: 1 }], "", undefined));
  expect(state.items).toHaveLength(1);

   redux-persist — holatni localStorage'ga saqlash (yangilanganda qoladi — whitelist bilan tanlab)
   Reducer SOF  test oson: (state, action)  kutilgan state (mock kam kerak)

redux-persist va test — Redux'ning ikki amaliy jihati. (1) redux-persist — store holatini localStorage (yoki boshqa saqlash) ga avtomatik saqlaydigan kutubxona: sahifa yangilanganda yoki brauzer yopilib ochilganda holat qayta tiklanadi (masalan savat, auth token). persistReducer(config, rootReducer) — reducer'ni "persist"ga o'raydi; persistStore(store) — persistor yaratadi; whitelist (yoki blacklist) — qaysi slice'lar saqlanishini tanlaydi (hammasi emas — masalan faqat cart, auth). React tomonda <PersistGate> holat tiklanguncha kutadi. Muhim nuqta: persist action'lari (persist/PERSIST, persist/REHYDRATE) non-serializable ma'lumot uzatadi, shuning uchun serializableCheck.ignoredActionsga qo'shiladi (Misol 13'dagi naqsh — 2.8). (2) Test — Redux'ning bashoratliligining bevosita foydasi: reducer sof funksiya (11.6: 2.3) bo'lgani uchun testi juda oddiy — kirish state va action berasiz, kutilgan chiquvchi state'ni tekshirasiz (expect(counterReducer({ value: 0 }, increment())).toEqual({ value: 1 })), hech qanday mock, DOM yoki render kerak emas. createAsyncThunk uchun fetch'ni mock qilib, fulfilled/rejected action'larini reducer'ga to'g'ridan uzatib holatni tekshirasiz (yoki butun thunk'ni store.dispatch bilan integratsion sinash). Ikki nuqta: (1) redux-persist — holatni saqlash (yangilanganda qoladi — whitelist bilan tanlab, persist action'larini serializableCheckda e'tiborsiz qoldirib); (2) sof reducer test oson (mock kam, tez, ishonchli) — bu Redux arxitekturasining muhim yutug'i. Bular — Redux ilovasini production'ga tayyorlashning ikki muhim bosqichi.


3. Sintaksis — tez ma'lumotnoma

text
SLICE 2.4-bob:       createSlice({ name, initialState, reducers: { add: (s,a) => {...} } })
STORE 2.4-bob:       configureStore({ reducer: { counter: counterReducer } })
PROVIDER 2.3-bob:    <Provider store={store}><App/></Provider>
ACTION 2.4-bob:      export const { increment } = slice.actions
SELECT 2.6-bob:      const x = useSelector((s) => s.counter.value)
DISPATCH 2.6-bob:    const d = useDispatch(); d(increment()) / d(addBy(5))
IMMER 2.5-bob:       reducers: { add: (s,a) => { s.items.push(a.payload) } }  // mutatsiya OK
THUNK 2.7-bob:       createAsyncThunk("x/fetch", async () => (await fetch()).json())
                   extraReducers: builder => builder.addCase(t.pending/fulfilled/rejected)
ENTITY 2.9-bob:      createEntityAdapter() — addOne/updateOne/removeOne + selectors
SELECTOR 2.10-bob:   createSelector([inputSel], (input) => derived)  // memoized
TYPED 2.11-bob:      RootState = ReturnType<typeof store.getState>; useAppSelector
PREPARE 2.12-bob:    reducers: { add: { reducer: (s,a)=>{...}, prepare: (x)=>({payload:...}) } }
SHALLOW 2.12-bob:    useSelector(s => ({a,b}), shallowEqual)  // obyekt selektor
LISTENER 2.12-bob:   createListenerMiddleware(); l.startListening({ actionCreator, effect })
PERSIST 2.13-bob:    persistReducer(cfg, rootReducer); persistStore(store) + <PersistGate>
TEST 2.13-bob:       expect(reducer(state, action())).toEqual(nextState)  // sof reducer

4. Batafsil kod namunalari

Misol 1 — Counter slice (asosiy — 2.4, 2.5)

ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";

interface CounterState { value: number; }
const initialState: CounterState = { value: 0 };

const counterSlice = createSlice({
  name: "counter",
  initialState,
  reducers: {
    increment: (state) => { state.value += 1; },                    // Immer — mutatsiya OK (2.5)
    decrement: (state) => { state.value -= 1; },
    incrementByAmount: (state, action: PayloadAction<number>) => {  // typed payload
      state.value += action.payload;
    },
    reset: (state) => { state.value = 0; },
  },
});

export const { increment, decrement, incrementByAmount, reset } = counterSlice.actions;
export default counterSlice.reducer;

Misol 2 — Store sozlash (2.4, 2.11)

ts
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "../features/counter/counterSlice";
import cartReducer from "../features/cart/cartSlice";

export const store = configureStore({
  reducer: {
    counter: counterReducer,
    cart: cartReducer,
  },
  // DevTools, thunk, immutability tekshiruvi — AVTOMATIK (2.4, 2.8)
});

export type RootState = ReturnType<typeof store.getState>;   // typed (2.11)
export type AppDispatch = typeof store.dispatch;

Misol 3 — Provider va typed hooks (2.3, 2.11)

tsx
// main.tsx
import { Provider } from "react-redux";
import { store } from "./app/store";

createRoot(document.getElementById("root")!).render(
  <Provider store={store}>      {/* store'ni ilovaga ulaydi */}
    <App />
  </Provider>
);

// app/hooks.ts — typed hooklar 11.14-bob:
import { useDispatch, useSelector, type TypedUseSelectorHook } from "react-redux";
import type { RootState, AppDispatch } from "./store";

export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

Misol 4 — Komponentda ishlatish (useSelector/useDispatch — 2.6)

tsx
import { useAppSelector, useAppDispatch } from "../app/hooks";
import { increment, decrement, incrementByAmount } from "./counterSlice";

function Counter() {
  const count = useAppSelector((state) => state.counter.value);   // o'qish (faqat counter o'zgarsa render)
  const dispatch = useAppDispatch();

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
      <button onClick={() => dispatch(incrementByAmount(5))}>+5</button>
    </div>
  );
}

Misol 5 — Cart slice (massiv — Immer — 2.5)

ts
interface CartItem { id: string; name: string; price: number; qty: number; }
interface CartState { items: CartItem[]; }

const cartSlice = createSlice({
  name: "cart",
  initialState: { items: [] } as CartState,
  reducers: {
    addItem: (state, action: PayloadAction<CartItem>) => {
      const existing = state.items.find((i) => i.id === action.payload.id);
      if (existing) existing.qty += 1;                       // Immer — to'g'ridan o'zgartirish OK
      else state.items.push({ ...action.payload, qty: 1 });  // push OK (2.5)
    },
    removeItem: (state, action: PayloadAction<string>) => {
      state.items = state.items.filter((i) => i.id !== action.payload);
    },
    updateQty: (state, action: PayloadAction<{ id: string; qty: number }>) => {
      const item = state.items.find((i) => i.id === action.payload.id);
      if (item) item.qty = action.payload.qty;
    },
    clearCart: (state) => { state.items = []; },
  },
});
export const { addItem, removeItem, updateQty, clearCart } = cartSlice.actions;
export default cartSlice.reducer;

Misol 6 — createAsyncThunk (async — 2.7)

ts
import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";

export const fetchUsers = createAsyncThunk("users/fetch", async (_, { rejectWithValue }) => {
  try {
    const res = await fetch("/api/users");
    if (!res.ok) throw new Error("Server xatosi");
    return await res.json();                    // fulfilled payload
  } catch (err) {
    return rejectWithValue((err as Error).message);   // rejected payload
  }
});

interface UsersState { items: User[]; loading: boolean; error: string | null; }
const usersSlice = createSlice({
  name: "users",
  initialState: { items: [], loading: false, error: null } as UsersState,
  reducers: {},
  extraReducers: (builder) => {
    builder
      .addCase(fetchUsers.pending, (state) => { state.loading = true; state.error = null; })
      .addCase(fetchUsers.fulfilled, (state, action) => {
        state.loading = false; state.items = action.payload;
      })
      .addCase(fetchUsers.rejected, (state, action) => {
        state.loading = false; state.error = action.payload as string;
      });
  },
});
export default usersSlice.reducer;
// Komponentda: useEffect(() => { dispatch(fetchUsers()); }, []);

Misol 7 — Async thunk komponentda (loading/error — 2.7)

tsx
function UsersList() {
  const { items, loading, error } = useAppSelector((state) => state.users);
  const dispatch = useAppDispatch();

  useEffect(() => {
    dispatch(fetchUsers());                     // pending  fulfilled/rejected (avtomatik)
  }, [dispatch]);

  if (loading) return <Spinner />;
  if (error) return <p className="error">{error}</p>;
  return <ul>{items.map((u) => <li key={u.id}>{u.name}</li>)}</ul>;
}
//  Lekin server ma'lumoti uchun RTK Query 12.3-bob ANCHA yaxshiroq (kesh/sync — 2.7)

Misol 8 — Memoized selektor (createSelector — 2.10)

ts
import { createSelector } from "@reduxjs/toolkit";
import type { RootState } from "../app/store";

//  const total = useSelector(s => s.cart.items.reduce(...))  — har render yangi hisob

//  memoized — faqat items o'zgarganda qayta hisob:
export const selectCartTotal = createSelector(
  (state: RootState) => state.cart.items,
  (items) => items.reduce((sum, item) => sum + item.price * item.qty, 0)
);
export const selectCartCount = createSelector(
  (state: RootState) => state.cart.items,
  (items) => items.reduce((sum, item) => sum + item.qty, 0)
);

// Komponentda:
const total = useAppSelector(selectCartTotal);   // memoized (kerakli render)

Misol 9 — createEntityAdapter (normalizatsiya — 2.9)

ts
import { createEntityAdapter, createSlice } from "@reduxjs/toolkit";

const productsAdapter = createEntityAdapter<Product>();   // ID  obyekt (normalize)

const productsSlice = createSlice({
  name: "products",
  initialState: productsAdapter.getInitialState({ loading: false }),   // {ids, entities, loading}
  reducers: {
    addProduct: productsAdapter.addOne,             // tayyor CRUD reducer (2.9)
    updateProduct: productsAdapter.updateOne,
    removeProduct: productsAdapter.removeOne,
    setProducts: productsAdapter.setAll,
  },
});
export default productsSlice.reducer;

// Avtomatik selektorlar:
export const { selectAll: selectAllProducts, selectById: selectProductById } =
  productsAdapter.getSelectors((state: RootState) => state.products);
//  O(1) qidiruv/yangilash (entities["id"]) — katta ro'yxat uchun (2.9)

Misol 10 — Auth slice (login/logout — 2.4)

ts
interface AuthState { user: User | null; token: string | null; }
const authSlice = createSlice({
  name: "auth",
  initialState: { user: null, token: localStorage.getItem("token") } as AuthState,
  reducers: {
    setCredentials: (state, action: PayloadAction<{ user: User; token: string }>) => {
      state.user = action.payload.user;
      state.token = action.payload.token;
      localStorage.setItem("token", action.payload.token);   // persist (side effect — odatda thunk'da)
    },
    logout: (state) => {
      state.user = null; state.token = null;
      localStorage.removeItem("token");
    },
  },
});
export const { setCredentials, logout } = authSlice.actions;
export default authSlice.reducer;

Misol 11 — Custom middleware (logging — 2.8)

ts
import { Middleware } from "@reduxjs/toolkit";

const loggerMiddleware: Middleware = (store) => (next) => (action) => {
  console.group(action.type);
  console.log("oldingi state:", store.getState());
  const result = next(action);                  // keyingi middleware/reducer'ga uzat
  console.log("yangi state:", store.getState());
  console.groupEnd();
  return result;
};

// store'ga qo'shish:
const store = configureStore({
  reducer: { /* ... */ },
  middleware: (getDefault) => getDefault().concat(loggerMiddleware),   // default + custom
});
//  Middleware — action oqimiga oraliq mantiq (logging, analytics — 2.8)

Misol 12 — Slice'lar orasida bog'liqlik (extraReducers — 2.7)

ts
// logout bo'lganda cart ham tozalansin (boshqa slice action'iga reaksiya):
const cartSlice = createSlice({
  name: "cart",
  initialState: { items: [] } as CartState,
  reducers: { /* add, remove... */ },
  extraReducers: (builder) => {
    builder.addCase(logout, (state) => {        // auth slice'ning logout action'iga
      state.items = [];                          // logout  savat tozalansin
    });
  },
});
//  extraReducers — boshqa slice action'iga reaksiya (slice'lar koordinatsiyasi — 2.7)

Misol 13 — To'liq store (ko'p slice — 2.2, 2.3)

ts
import { configureStore } from "@reduxjs/toolkit";
import authReducer from "../features/auth/authSlice";
import cartReducer from "../features/cart/cartSlice";
import productsReducer from "../features/products/productsSlice";

export const store = configureStore({
  reducer: {
    auth: authReducer,
    cart: cartReducer,
    products: productsReducer,
  },
  middleware: (getDefault) =>
    getDefault({
      serializableCheck: { ignoredActions: ["persist/PERSIST"] },   // ba'zi action'larni e'tiborga olmaydi
    }),
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
//  Feature-based: har slice o'z papkasida, store'da birlashadi (2.3)

Misol 14 — Redux vs Context vs Zustand qaror (2.11)

ts
// CLIENT STATE — qaysi vosita:

// 1. Kam o'zgaradigan global  Context 12.1-bob:
//    theme, locale, auth — Context yetadi (oddiy)

// 2. Oddiy global state  Zustand (12.5 — kam boilerplate):
//    const useStore = create((set) => ({ count: 0, inc: () => set(s => ({count: s.count+1})) }))

// 3. Katta/murakkab/jamoaviy  Redux Toolkit (bu bob):
//    ko'p slice, DevTools, middleware, qat'iy struktura

// SERVER STATE — Redux EMAS:
// 4. API ma'lumoti  RTK Query 12.3-bob yoki TanStack Query 12.4-bob:
//    kesh, fonda yangilash, qayta urinish — AVTOMATIK (Redux thunk'dan yaxshiroq)

//  To'g'ri vosita: kamContext, oddiyZustand, kattaRedux, serverQuery (2.11, 12.1: 2.10)

Misol 15 — prepare callback va listener middleware (2.12)

ts
import { createSlice, nanoid, createListenerMiddleware } from "@reduxjs/toolkit";

// prepare — payload'ga id/timestamp qo'shish (reducer SOF qoladi):
const todosSlice = createSlice({
  name: "todos",
  initialState: [] as Todo[],
  reducers: {
    addTodo: {
      reducer: (state, action: PayloadAction<Todo>) => { state.push(action.payload); },
      prepare: (text: string) => ({
        payload: { id: nanoid(), text, done: false, createdAt: Date.now() },  // hosilaviy maydonlar
      }),
    },
  },
});
export const { addTodo } = todosSlice.actions;
// dispatch(addTodo("Non olish"))   prepare id/createdAt/done qo'shadi

// listener middleware — logout'ga reaksiya (side effect — thunk'ning muqobili):
export const listenerMiddleware = createListenerMiddleware();
listenerMiddleware.startListening({
  actionCreator: logout,                         // logout dispatch bo'lganda...
  effect: async (_action, api) => {              // ...bu effekt ishlaydi
    localStorage.removeItem("token");            // side effect (tozalash)
    api.dispatch(clearCart());                   // boshqa action ham chaqirish mumkin
  },
});
// store'ga: middleware: (gd) => gd().prepend(listenerMiddleware.middleware)
//  prepare — payload tayyorlash; listener — action'ga reaksiya (2.12)

Misol 16 — obyekt selektor va shallowEqual (2.12, 2.10)

tsx
import { shallowEqual } from "react-redux";

function UserBadge() {
  //  har safar yangi obyekt  har render re-render:
  // const { name, avatar } = useAppSelector(s => ({ name: s.auth.user?.name, avatar: s.auth.user?.avatar }));

  //  1-yo'l: shallowEqual (sayoz solishtir — maydonlar bir xil bo'lsa render yo'q):
  const { name, avatar } = useAppSelector(
    (s) => ({ name: s.auth.user?.name, avatar: s.auth.user?.avatar }),
    shallowEqual
  );

  //  2-yo'l (odatda afzal): alohida primitiv selektorlar:
  // const name = useAppSelector(s => s.auth.user?.name);
  // const avatar = useAppSelector(s => s.auth.user?.avatar);

  return <div><img src={avatar} alt="" /> {name}</div>;
}
//  Obyekt qaytaruvchi selektor  shallowEqual yoki alohida selektor (ortiqcha render — 2.10)

Misol 17 — redux-persist va slice/thunk test (2.13)

ts
// --- redux-persist (holatni localStorage'ga saqlash) ---
import { combineReducers, configureStore } from "@reduxjs/toolkit";
import { persistStore, persistReducer } from "redux-persist";
import storage from "redux-persist/lib/storage";        // localStorage

const rootReducer = combineReducers({ cart: cartReducer, auth: authReducer });
const persistConfig = { key: "root", storage, whitelist: ["cart", "auth"] };  // faqat shu slice'lar
const persistedReducer = persistReducer(persistConfig, rootReducer);

export const store = configureStore({
  reducer: persistedReducer,
  middleware: (gd) =>
    gd({ serializableCheck: { ignoredActions: ["persist/PERSIST", "persist/REHYDRATE"] } }),
});
export const persistor = persistStore(store);
// React: <PersistGate loading={null} persistor={persistor}><App/></PersistGate>

// --- test (Vitest/Jest) — sof reducer  mock kam, test oson ---
import { describe, it, expect } from "vitest";
import counterReducer, { increment, incrementByAmount } from "./counterSlice";

describe("counterSlice", () => {
  it("increment 1 ga oshiradi", () => {
    expect(counterReducer({ value: 0 }, increment())).toEqual({ value: 1 });
  });
  it("incrementByAmount payload qo'shadi", () => {
    expect(counterReducer({ value: 2 }, incrementByAmount(5))).toEqual({ value: 7 });
  });
});

// thunk holatlarini reducer'ga to'g'ridan uzatib tekshirish:
it("fetchUsers.fulfilled items'ni to'ldiradi", () => {
  const initial = { items: [], loading: true, error: null };
  const next = usersReducer(initial, fetchUsers.fulfilled([{ id: "1", name: "Ali" }], "req"));
  expect(next.loading).toBe(false);
  expect(next.items).toHaveLength(1);
});
//  redux-persist — holat saqlash (whitelist); reducer sof  test oddiy (2.13)

5. To'g'ri va noto'g'ri holatlar

1) Redux versiyasi

text
 qo'lda Redux (action type konstanta, switch reducer — boilerplate)
 Redux Toolkit (createSlice — avtomatik — 2.4)

2) Immutability

text
 createSlice tashqarisida mutatsiya (Immer yo'q — buziladi — 2.5)
 createSlice ICHIDA mutatsiya OK (Immer); tashqarida yangi nusxa

3) Selektor (hosilangan)

text
 useSelector(s => s.items.filter(...))  (har render yangi massiv — 2.10)
 createSelector (memoized)

4) Server ma'lumoti

text
 createAsyncThunk + slice'da API ma'lumoti saqlash (qo'lda kesh — 12.1: 2.10)
 RTK Query / TanStack Query (avtomatik kesh — 12.3, 12.4)

5) Hamma narsa Redux'da

text
 har kichik UI holati Redux'da (boilerplate, ortiqcha)
 lokal  useState; global  Redux (faqat kerakli — 2.1)

6) Type

text
 useSelector((s: any) => ...)  (type yo'q)
 useAppSelector (typed — RootState — 2.11)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — A non-serializable value was detected in the state

Sababi: state'da Promise/funksiya/Date saqlandi (2.8 serializability). Yechimi: faqat serializable (oddiy obyekt/massiv); Date — string sifatida; thunk natijasini to'g'rila.

Xato 2 — Komponent yangilanmaydi (state o'zgardi)

Sababi: mutatsiya createSlice tashqarisida, yoki yangi havola yo'q 2.5-bob. Yechimi: createSlice ICHIDA o'zgartir (Immer); selektor to'g'ri qism.

Xato 3 — Komponent har render re-render (sekin)

Sababi: useSelector yangi obyekt qaytaradi 2.10-bob. Yechimi: createSelector (memoized); yoki alohida primitiv selektor.

Xato 4 — Cannot find module 'react-redux' / Provider yo'q

Sababi: Provider o'ralmagan, yoki react-redux o'rnatilmagan. Yechimi: npm i @reduxjs/toolkit react-redux; <Provider store={store}> (Misol 3).

Xato 5 — Async thunk loading boshqarilmaydi

Sababi: extraReducers'da pending/fulfilled/rejected yo'q 2.7-bob. Yechimi: uchchovini ham boshqar (Misol 6).

Xato 6 — Redux DevTools ko'rinmaydi

Sababi: brauzer kengaytmasi yo'q yoki production. Yechimi: Redux DevTools kengaytmasini o'rnat (RTK avtomatik ulaydi — dev'da — 2.8).

Xato 7 — Hamma joyda type any

Sababi: typed hooks yo'q 2.11-bob. Yechimi: useAppSelector/useAppDispatch (RootState/AppDispatch — Misol 3).

Xato 8 — redux-persist bilan A non-serializable value ogohlantirishi

Sababi: persist ichki action'lari (persist/PERSIST, persist/REHYDRATE) non-serializable ma'lumot uzatadi 2.13-bob. Yechimi: serializableCheck.ignoredActionsga persist action'larini qo'sh (Misol 17, 2.13).

Xato 9 — Reducer ichida Date.now()/Math.random() (test beqaror)

Sababi: reducer nosof bo'lib qoladi — bir xil action har xil natija 2.12-bob. Yechimi: hosilaviy qiymatlarni prepare callback'da tayyorla (id, timestamp — reducer sof qoladi — Misol 15, 2.12).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Context + useReducer 12.1-bob: Redux — uning kengaytmasi (markaziy reducer + selektor).
  • RTK Query 12.3-bob: RTK ustiga qurilgan server-state vositasi (data fetching).
  • TanStack Query 12.4-bob: server state — Redux'dan ajratilgan (client vs server).
  • Zustand 12.5-bob: oddiy global state — Redux'ning yengil muqobili.
  • useReducer 11.6-bob: slice reducer — useReducer'ning Redux versiyasi (Immer bilan).
  • TypeScript 11.14-bob: typed hooks, PayloadAction, RootState.
  • Performance 11.11-bob: selektor (createSelector, shallowEqual) — re-render optimizatsiyasi.
  • Backend (8-QISM): thunk — API'ga ulanadi (lekin Query afzal — 12.3).
  • Testlash: sof reducer oddiy birlik test (mock kam — 2.13).

8. Eng yaxshi amaliyotlar (best practices)

  • Redux Toolkit (qo'lda Redux emas) — createSlice/configureStore 2.4-bob.
  • Immer createSlice ichida (mutatsiya sintaksisi — tashqarida yangi nusxa — 2.5).
  • Typed hooks (useAppSelector/useAppDispatch — type-safe — 2.11).
  • createSelector hosilangan qiymatga (memoized — re-render — 2.10).
  • Server ma'lumotini Query'da (RTK/TanStack Query — Redux thunk emas — 12.1: 2.10).
  • Faqat global state Redux'da (lokal useState — 2.1).
  • Feature-based slice'lar (har feature o'z papkasida — 2.3).
  • createEntityAdapter ro'yxatga (normalize — O(1) — 2.9).
  • createAsyncThunk uch holat (pending/fulfilled/rejected — 2.7).
  • DevTools'dan foydalanish (vaqt sayohati — debugging — 2.8).
  • prepare callback hosilaviy payload'ga (id/timestamp — reducer sof — 2.12).
  • Side effect'ga listener middleware (createListenerMiddleware — action reaksiyasi — 2.12).
  • Kerakli slice'ni redux-persist bilan saqlash (whitelist — 2.13).
  • Slice/thunk'ni test yozib qoplash (sof reducer — 2.13).

9. Amaliy loyiha: "E-commerce Holat Boshqaruvi (Redux Toolkit)"

Redux Toolkit'ni real, murakkab holatda mustahkamlash.

Maqsad

E-commerce ilovasi uchun to'liq Redux store yarat: auth, cart, products (entity), wishlist — slice'lar, thunk, selektorlar bilan.

Talablar (requirements)

  1. Store: configureStore + ko'p slice (auth/cart/products — Misol 2, 13).
  2. Typed hooks: useAppSelector/useAppDispatch (Misol 3, 2.11).
  3. Cart slice: add/remove/updateQty/clear (Immer — Misol 5).
  4. createSelector: cart total/count (memoized — Misol 8, 2.10).
  5. createAsyncThunk: mahsulotlarni API'dan ol (pending/fulfilled/rejected — Misol 6, 7).
  6. createEntityAdapter: products normalize (O(1) — Misol 9, 2.9).
  7. Slice koordinatsiyasi: logout cart tozalansin (extraReducers — Misol 12).
  8. Auth slice: setCredentials/logout + persist (Misol 10).
  9. DevTools: vaqt sayohati bilan tekshir (action oqimi — 2.8).
  10. Provider: ilovani Provider bilan o'ra (Misol 3).

Maslahatlar (hint)

  • createSlice ICHIDA mutatsiya (Immer — tashqarida emas — Xato 2).
  • Hosilangan qiymat (total) — createSelector (har render emas — Misol 8).
  • Server ma'lumoti uchun thunk ishlaydi, lekin keyingi bobda RTK Query yaxshiroq 2.7-bob.
  • Typed hooks — har joyda useAppSelector (any emas — Xato 7).
  • DevTools bilan har action'ni kuzat (debugging — 2.8).
  • Slice'larni feature papkalariga ajrat 2.3-bob.

"Tayyor" mezonlari (acceptance criteria)

  • Store + ko'p slice (auth/cart/products).
  • Typed hooks (useAppSelector/useAppDispatch).
  • Cart CRUD (Immer bilan).
  • createSelector (total/count memoized).
  • createAsyncThunk (3 holat).
  • createEntityAdapter (products normalize).
  • Slice koordinatsiyasi (logout cart).
  • DevTools'da action oqimi ko'rinadi.
  • Provider o'ralgan, ilova ishlaydi.

Yechim kodi ataylab berilmagan — bu loyihani o'zingiz yozib ko'ring.


10. Xulosa va keyingi bobga ko'prik

Bu bobda Redux Toolkit'ni chuqur o'rgandik:

  • Nega Redux (Context cheklovi — 2.1); asosiy tushunchalar (store/action/reducer — Flux — 2.2); qatlamlar 2.3-bob; configureStore/createSlice (RTK — 2.4); Immer 2.5-bob.
  • useSelector/useDispatch (tabiiy selektor — 2.6); createAsyncThunk (async — 2.7); middleware/DevTools 2.8-bob; createEntityAdapter (normalize — 2.9).
  • createSelector (memoized — 2.10); TypeScript va qachon Redux 2.11-bob.
  • prepare callback, shallowEqual, listener middleware 2.12-bob; redux-persist va test 2.13-bob.

Endi siz katta, murakkab ilova holatini Redux Toolkit bilan markaziy, bashoratli, kuzatiladigan tarzda boshqara olasiz — slice, thunk, selektor, DevTools. Eng muhimi — client state (Redux) va server state (Query) farqini bilasiz.

Keyingi bob — 12.3-bob: RTK Query. Redux'da server ma'lumotini createAsyncThunk bilan boshqarish mumkin, lekin u ko'p qo'lda kod (loading/error/kesh) talab qiladi. RTK Query — Redux Toolkit'ning server-state yechimi (data fetching va caching): bir necha qator bilan API endpoint'larini belgilaysiz, va u kesh, fonda yangilash, qayta urinish, loading/error, deduplication — barchasini avtomatik qiladi. Bu — server ma'lumotini professional boshqarishning Redux ekotizimidagi yechimi (TanStack Query — 12.4 — bilan raqobatda).


Foydalanilgan rasmiy/ishonchli manbalar

  • Redux Toolkit rasmiy hujjaticonfigureStore, createSlice, createAsyncThunk, createEntityAdapter, createSelector, createListenerMiddleware, prepare callback, TypeScript qo'llanmasi
  • Redux rasmiy hujjati — "Redux Essentials" darsligi, rasmiy "Style Guide" (best practices), Flux/bir tomonlama oqim tamoyillari, typed hooks (Quick Start)
  • React-Redux rasmiy hujjatiProvider, useSelector, useDispatch, shallowEqual, TypedUseSelectorHook, re-render optimizatsiyasi
  • Immer rasmiy hujjati — "draft" mexanizmi, immutable natija, ishlatish chegaralari
  • Reselect rasmiy hujjati — memoized selektor, input/output selektor, hosilangan holat
  • redux-persist rasmiy hujjatipersistReducer, persistStore, PersistGate, whitelist/blacklist
  • Redux DevTools — vaqt sayohati (time-travel), action va state inspeksiyasi
  • Redux jamoasi maqolalari (Mark Erikson) — "When (and when not) to reach for Redux", RTK best practices, client vs server state

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
12.2-bob: Redux Toolkit (store, slice, async thunk) — Wisar