WisarWisar
Dasturlash kitobi/12-QISM — State Management34 daqiqa

12.1-bob: Context API (chuqur)

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


1. Kirish va motivatsiya

11-QISM (React)da biz Context'ni bir necha marta — auth (11.5, 11.15), theme 11.5-bob, custom hook bilan 11.7-bob, TypeScript bilan 11.14-bob — ko'rib o'tdik. Endi 12-QISM (State Management)ning birinchi bobida unga chuqur kiramiz: Context aslida nima, ichida qanday ishlaydi, re-render xulqi (eng muhim va eng ko'p tuzoq beruvchi mavzu), uni to'g'ri optimallashtirish, va eng muhimi — qachon Context yetarli, qachon Redux/Zustand kerak. Bu — state management mavzusining poydevori: Context'ni chuqur tushunmasdan, Redux yoki Zustand'ni to'g'ri tanlab bo'lmaydi.

State management — bu kitobning eng ko'p "noto'g'ri tushunilgan" mavzularidan biri. Ko'p dasturchi "global state kerak bo'lsa Redux ishlat" deb o'ylaydi, lekin aslida ko'p holatda Context yetarli (yoki hatto kerak emas — ma'lumot serverdan bo'lsa, u TanStack Query'ning ishi — 12.4). Boshqalar Context'ni hamma narsaga ishlab, performance muammolariga duch keladi. Haqiqat — balansda: har vositaning o'z o'rni bor. Bu bobda Context'ning kuchli tomonlarini (oddiy, o'rnatilgan, hech narsa o'rnatish kerak emas) va cheklovlarini (re-render, selektor yo'q) aniq tushunamiz — toki keyingi boblarda (Redux, Zustand, Query) to'g'ri qaror qabul qila olasiz.

Bu bob: Context muammosi (props drilling — chuqur), Context arxitekturasi (createContext/Provider/useContext — ichki mexanizm), re-render xulqi (Context nega va qachon re-render keltiradi — eng muhim), value barqarorligi (memo — 11.6: 2.9), Context bo'lish (state + dispatch ajratish), Context + useReducer (mini-Redux), ko'p Context va provider kompozitsiyasi, performance tuzoqlari va yechimlari (selektor naqshi, use-context-selector), type-safe Context (11.14 davomi), qachon Context, qachon Redux/Zustand/Query (eng muhim qaror), va Server Components va Context (Next.js — 13). Mavzuni to'liq, performance va arxitektura fokusi bilan ochamiz.

O'xshatish: Context — bu binoning markaziy radio-eshittirish tizimi. Props drilling (Context'siz) — bu xabarni qo'ldan-qo'lga uzatish: direktor (yuqori komponent) xabarni o'rinbosariga, u bo'lim boshlig'iga, u xodimga... har qatlam orqali (zerikarli, biror joyda unutiladi). Context — bu markaziy karnay: direktor bir marta e'lon qiladi (Provider), va istalgan xonadagi istalgan xodim (consumer) uni to'g'ridan eshitadi (oraliq qatlamlar orqali emas). LEKIN — bu karnayning o'ziga xosligi bor: karnay bir narsani o'zgartirsa (value yangilansa), uni tinglab turgan barcha xona (consumer) e'tibor beradi (re-render) — hatto o'zgargan qism unga tegishli bo'lmasa ham. Shuning uchun bitta katta karnay (hamma narsani eshittiruvchi) o'rniga, bir necha alohida karnay (har biri o'z mavzusi — auth, theme, savat) ishlatish afzal — toki savat o'zgarganda faqat savatni tinglab turganlar e'tibor bersin (auth'ni tinglab turganlar emas).

Nega muhim?

  • State management poydevori — Context'ni chuqur bilmasdan Redux/Zustand qachon kerakligini tushunmaysiz.
  • Performance — Context re-render tuzog'i — eng keng frontend performance muammosi (11.11 davomi).
  • O'rnatilgan, bepul — Context React'ning o'zida (kutubxona emas) — oddiy global state uchun ideal.
  • To'g'ri qaror — "Redux kerakmi yoki Context yetadimi?" — senior darajadagi dasturchi har kuni javob beradigan savol.

2. Nazariya — chuqur tushuntirish

2.1. Props drilling muammosi (chuqur)

text
  PROPS DRILLING — ma'lumotni faqat CHUQURDAGI komponentga yetkazish uchun
  har ORALIQ qatlamdan qo'lda uzatish (zerikarli, mo'rt):

  App (user)
   │ props: user
   ▼
  Layout (user) ──────── oraliq (user'ni ishlatmaydi, faqat uzatadi)
   │ props: user
   ▼
  Sidebar (user) ─────── oraliq (user'ni ishlatmaydi)
   │ props: user
   ▼
  UserMenu (user) ────── BU YERDA kerak (lekin 3 qatlam orqali keldi!)

  MUAMMOLAR:
   Oraliq komponentlar keraksiz props oladi (Layout/Sidebar user'ni bilmasin edi)
   Yangi prop qo'shsa — HAR qatlamni o'zgartirish (mo'rt)
   O'qish qiyin (prop qaydan keldi? — 5 fayl orqali)
   Refactoring og'riqli (komponent ko'chsa — drilling buziladi)

   Props drilling — 2-3 qatlamda maqbul; CHUQUR (5+) yoki KENG (ko'p komponent) — Context
   Context — ma'lumotni MANBADAN to'g'ridan consumer'ga (oraliq qatlamlar o'tkaz emas)

Props drilling muammosi — Context'ning asosiy sababi. Props drilling — ma'lumotni (masalan user) faqat chuqurdagi bir komponentga yetkazish uchun har oraliq qatlamdan qo'lda uzatish. Misol: user App'da, lekin UserMenu'ga kerak — u App Layout Sidebar UserMenu zanjiri orqali uzatiladi, va Layout/Sidebar user'ni ishlatmaydi (faqat pastga uzatadi — "o'tkazuvchi"). Muammolar: (1) oraliq komponentlar keraksiz props oladi (Layout user haqida bilishi shart emas edi); (2) yangi prop qo'shsangiz — har qatlamni o'zgartirish (mo'rt, ko'p fayl); (3) o'qish qiyin (prop qaydan keldi? — bir necha fayl orqali kuzatish); (4) refactoring og'riqli (komponent boshqa joyga ko'chsa, drilling zanjiri buziladi). Ikki nuqta: (1) props drilling 2-3 qatlamda maqbul (oddiy, aniq — ortiqcha murakkablik kerak emas); lekin chuqur (5+ qatlam) yoki keng (ko'p komponent bir ma'lumotni ulashadi) bo'lsa — Context; (2) Context ma'lumotni manbadan to'g'ridan consumer'ga uzatadi (oraliq qatlamlar "o'tkazuvchi" bo'lmaydi — ular ma'lumot haqida bilmaydi ham). Bu — Context'ning asosiy qiymati.

2.2. Context arxitekturasi — ichki mexanizm

text
  3 QISM (11.5: 2.11 — chuqurroq):

  1. createContext — kontekst "kanali" yaratiladi (default qiymat bilan):
  const ThemeContext = createContext("light");   // default — Provider yo'q bo'lsa ishlatiladi

  2. Provider — ma'lumot MANBASI (qiymatni pastga beradi):
  <ThemeContext.Provider value={theme}>
    {/* bu ichdagi BARCHA komponent theme'ni o'qiy oladi */}
  </ThemeContext.Provider>

  3. useContext — istalgan chuqurlikdan O'QISH (consumer):
  const theme = useContext(ThemeContext);   // eng yaqin Provider'ning value'sini oladi

  ICHKI MEXANIZM (qanday ishlaydi):
  - React har Context uchun ichki "qiymat egasi" (Provider) saqlaydi
  - useContext(Ctx) — komponent daraxtida YUQORIGA qarab eng yaqin Provider'ni topadi
  - Provider value o'zgarsa  React shu Context'ni ishlatgan BARCHA consumer'ni re-render qiladi (2.3)

   useContext eng YAQIN Provider'ni oladi (ichma-ich Provider — ichki g'olib)
   Provider yo'q bo'lsa — createContext'dagi DEFAULT qiymat (yoki null — type-safe — 11.14)

Context arxitekturasi — uch qism va ichki mexanizm (11.5: 2.11 chuqurroq). (1) createContext(default) — kontekst "kanali"ni yaratadi (default qiymat bilan — Provider topilmasa shu ishlatiladi). (2) <Context.Provider value={...}> — ma'lumot manbasi: u value'ni o'z ichidagi butun daraxtga beradi. (3) useContext(Context) — istalgan chuqurlikdan o'qish (consumer): u eng yaqin Provider'ning value'sini oladi. Ichki mexanizm: React har Context uchun ichki holatda Provider qiymatini saqlaydi; useContext(Ctx) chaqirilganda, React komponentlar daraxtida yuqoriga qarab eng yaqin Ctx.Providerni topadi va uning value'sini qaytaradi; va eng muhimi — Provider value o'zgarganda React o'sha Context'ni ishlatgan barcha consumer'ni re-render qiladi (2.3 — bu re-render xulqi). Ikki nuqta: (1) useContext eng yaqin Provider'ni oladi (ichma-ich Provider bo'lsa, ichki g'olib — bu nested theme/locale uchun foydali); (2) Provider yo'q bo'lsa — createContext'dagi default qiymat (yoki null — type-safe Context'da null tekshiruv bilan — 11.14: 2.13). Bu mexanizmni tushunish — Context'ning to'g'ri ishlatish va optimallashtirishning asosi.

2.3. Re-render xulqi — eng muhim tushuncha

text
  ENG MUHIM (va eng ko'p tuzoq): Provider value O'ZGARganda — o'sha Context'ni
  ishlatgan BARCHA consumer RE-RENDER bo'ladi (qism o'zgarmagan bo'lsa HAM):

  <AppContext.Provider value={{ user, theme, cart }}>
    <Profile />      user'ni ishlatadi (useContext(AppContext))
    <Header />       theme'ni ishlatadi
    <CartIcon />     cart'ni ishlatadi
  </AppContext.Provider>

  cart O'ZGARDI (faqat cart):
   value obyekti yangi  React: "value o'zgardi"
   Profile, Header, CartIcon — UCHCHOVI ham RE-RENDER (user/theme tegmagan bo'lsa ham!)
   Profile/Header bekorga render (cart ularga aloqasi yo'q)

  IKKI SABAB consumer re-render bo'ladi:
  1. Provider value o'zgardi (har consumer — yangi value oladi)
  2. value REFERENTIAL o'zgardi (har render yangi obyekt — 11.6: 2.9 — eng keng tuzoq)

   Context — SELEKTOR yo'q (value'ning bir QISMINI tinglab bo'lmaydi — hammasi yoki hech narsa)
   Katta/tez o'zgaradigan value  ko'p keraksiz re-render (Context cheklovi — 2.9, Redux/Zustand)

Re-render xulqi — Context'ning eng muhim va eng ko'p tuzoq beruvchi tushuncha. Qoida: Provider'ning value'si o'zgarganda, o'sha Context'ni ishlatgan barcha consumer re-render bo'ladi — value'ning faqat bir qismi o'zgarsa ham, va o'sha qism consumer'ga tegishli bo'lmasa ham. Misol: value={{ user, theme, cart }} — agar faqat cart o'zgarsa, Profile (user'ni ishlatadi), Header (theme), CartIcon (cart) — uchchovi re-render bo'ladi (Profile/Header bekorga — cart ularga aloqasi yo'q). Ikki sabab consumer re-render bo'ladi: (1) Provider value'si o'zgardi (har consumer yangi value oladi); (2) value referential o'zgardi — komponent ichida yaratilgan obyekt har render yangi havola (11.6: 2.9 — eng keng tuzoq — value har render yangidan yaralsa, consumer'lar har render re-render bo'ladi). Ikki kritik nuqta: (1) Context'da selektor yo'q — value'ning faqat bir qismini "tinglab" bo'lmaydi (hammasi yoki hech narsa — useContext butun value'ni oladi); (2) shuning uchun katta yoki tez-tez o'zgaradigan value ko'p keraksiz re-render keltiradi (Context'ning asosiy cheklovi) — bu yerda Context bo'lish 2.6-bob yoki Redux/Zustand (selektorli — 2.9) kerak bo'ladi. Bu tushunchani anglash — Context'ni to'g'ri ishlatishning va keyingi state-management qaroplarining kalitidir.

2.4. Value barqarorligi — memo bilan

text
  TUZOQ: Provider value har render YANGI obyekt  consumer'lar HAR render re-render:

   value har render yangi havola (11.6: 2.9):
  function AuthProvider({ children }) {
    const [user, setUser] = useState(null);
    return (
      <AuthContext.Provider value={{ user, setUser }}>   {/* HAR render YANGI obyekt! */}
        {children}                                         {/*  consumer'lar HAR render render */}
      </AuthContext.Provider>
    );
  }

   value'ni useMemo bilan barqarorlashtirish (11.6: 2.9):
  function AuthProvider({ children }) {
    const [user, setUser] = useState(null);
    const value = useMemo(() => ({ user, setUser }), [user]);   // user o'zgargandagina yangi
    return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
  }

  ┌────────────────────────────────────────────────────────────┐
  │ value={{ ... }}: har render yangi  consumer'lar bekor render│
  │ value=useMemo(...): user o'zgargandagina  kerakli render    │
  └────────────────────────────────────────────────────────────┘

   Provider value'ni DOIM useMemo bilan barqaror ushlash (har render yangi obyekt — tuzoq)
   setUser (setState) barqaror — useMemo deps'da kerak emas (faqat user)

Value barqarorligi — Context'ni to'g'ri ishlatishning majburiy qoidasi (11.6: 2.9 davomi). Tuzoq: agar Provider'ning value'sini JSX'da to'g'ridan obyekt sifatida yozsangiz (value={{ user, setUser }}), u har render yangi havola oladi (11.6: 2.9 — referential equality) — natijada consumer'lar har render re-render bo'ladi (garchi user o'zgarmasa ham), chunki React value'ni === bilan solishtiradi va "o'zgardi" deb biladi. Yechim — value'ni useMemo bilan barqaror ushlash: const value = useMemo(() => ({ user, setUser }), [user]) — endi value faqat user o'zgargandagina yangi obyekt bo'ladi (aks holda oldingi havola qaytariladi consumer'lar bekorga render bo'lmaydi). Ikki nuqta: (1) Provider value'ni doim useMemo bilan barqaror ushlang (har render yangi obyekt — eng keng Context tuzog'i); (2) setUser (yoki dispatch) — React barqaror kafolatlaydi (har render bir xil havola), shuning uchun uni useMemo deps'ga qo'shish shart emas (faqat o'zgaradigan qiymatlar — user). Bu — har Context Provider'ning standart naqshi (value memo). Memoizatsiya anti-naqshi (nozik nuqta): useMemo value re-render muammosini hal qilmaydi, balki uni kechiktiradi — value memo qilinsa ham, user haqiqatan o'zgarganda barcha consumer baribir re-render bo'ladi (value memo faqat keraksiz — o'zgarmagan — renderlarni to'xtatadi). Shuning uchun value memo — zarur, lekin yetarli emas optimizatsiya; agar user tez-tez o'zgarsa va consumer'lar ko'p bo'lsa, memo yordam bermaydi — bu yerda Context'ni bo'lish 2.6-bob yoki selektor 2.9-bob kerak. Yana bir anti-naqsh: value obyektining har maydonini alohida useMemo qilib, keyin ularni yana obyektga yig'ish — bu ortiqcha murakkablik (bitta useMemo(() => ({...}), [deps]) yetadi). Xulosa: value memo — Context'ning poydevor qoidasi, lekin performance'ni faqat u orqali "tuzatib" bo'lmaydi (arxitektura — bo'lish/selektor — muhimroq).

2.5. Context + custom hook — toza interfeys

text
  Context'ni TO'G'RIDAN ishlatish o'rniga — custom hook bilan o'rash (11.7, toza):

  //  Har komponentda: useContext(AuthContext) + null tekshiruv (takror)
  //  Custom hook — bir marta o'rab, qulay interfeys + xato himoyasi:
  const AuthContext = createContext(null);

  export function AuthProvider({ children }) {
    const [user, setUser] = useState(null);
    const login = useCallback((u) => setUser(u), []);
    const logout = useCallback(() => setUser(null), []);
    const value = useMemo(() => ({ user, login, logout }), [user, login, logout]);
    return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
  }

  export function useAuth() {                        // custom hook (qulay + xavfsiz)
    const ctx = useContext(AuthContext);
    if (!ctx) throw new Error("useAuth AuthProvider ichida ishlatilishi kerak");   // himoya
    return ctx;
  }

  // Ishlatish — toza:
  const { user, login, logout } = useAuth();        // useContext emas (qulay, type-safe)

   Custom hook — Context'ni o'rab, qulay (useAuth), xavfsiz (null xato), type-safe (11.14)
   Context'ni TO'G'RIDAN export QILMASLIK — custom hook orqali (interfeys nazorati)

Context + custom hook — Context'ni to'g'ri tashkil qilishning standart naqshi (11.7 bilan). Context'ni to'g'ridan (useContext(AuthContext)) ishlatish o'rniga, uni custom hook bilan o'raysiz. Sabablar: (1) har komponentda useContext + null tekshiruvni takrorlamaslik; (2) qulay nom (useAuth()useContext(AuthContext)dan toza); (3) xato himoyasi (Provider ichida ishlatilmasa — aniq xato xabari, undefined'dan yaxshiroq); (4) type-safe (11.14: 2.13). Naqsh: AuthProvider (value'ni memo bilan — 2.4), useAuth hook (useContext + if (!ctx) throw). Ishlatishda — const { user, login, logout } = useAuth() (toza, qulay). Ikki nuqta: (1) custom hook Context'ni o'rab, qulay/xavfsiz/type-safe interfeys beradi (har real loyiha shunday qiladi); (2) Context obyektini (AuthContext)ni to'g'ridan export qilmang — faqat useAuth hook va AuthProviderni export qiling (interfeys nazorati — foydalanuvchi Context bilan to'g'ridan ishlamasin, hook orqali). Bu — toza, qayta ishlatiladigan Context arxitekturasining poydevori.

2.6. Context bo'lish — state va dispatch ajratish

text
  PERFORMANCE NAQSHI: Context'ni bo'lish (re-render kamaytirish — 2.3):

  MUAMMO: bitta Context'da ham STATE, ham UPDATER — state o'zgarganda
          faqat updater'ni ishlatgan komponent ham re-render (keraksiz):

  YECHIM 1 — STATE va DISPATCH'ni alohida Context'ga ajratish:
  const StateContext = createContext(null);      // o'qish (tez-tez o'zgaradi)
  const DispatchContext = createContext(null);   // yozish (HECH o'zgarmaydi — barqaror)

  function Provider({ children }) {
    const [state, dispatch] = useReducer(reducer, initial);
    return (
      <DispatchContext.Provider value={dispatch}>   {/* dispatch barqaror — re-render yo'q */}
        <StateContext.Provider value={state}>
          {children}
        </StateContext.Provider>
      </DispatchContext.Provider>
    );
  }
  //  faqat dispatch'ni ishlatgan komponent (tugma) state o'zgarganda RE-RENDER BO'LMAYDI

  YECHIM 2 — mavzu bo'yicha bo'lish (auth/theme/cart alohida — 11.11: 2.9):
  <AuthContext><ThemeContext><CartContext>...   // cart o'zgarsa — auth/theme consumer tegmaydi

   State + dispatch ajratish — faqat dispatch'ni ishlatgan komponent state'ga reaksiya qilmaydi
   Mavzu bo'yicha bo'lish — bir mavzu o'zgarsa boshqasining consumer'lari tegmaydi

Context bo'lish — Context performance optimizatsiyasining eng kuchli naqshi (2.3 re-render muammosini hal qiladi). Naqsh 1 — state va dispatch ajratish: bitta Context'da ham state (o'qish), ham updater (dispatch/setState — yozish) bo'lsa, state o'zgarganda faqat dispatch'ni ishlatgan komponent (masalan "qo'shish" tugmasi — u state'ni o'qimaydi, faqat o'zgartiradi) ham re-render bo'ladi (keraksiz). Yechim — state'ni va dispatch'ni alohida Context'larga ajratish: StateContext (tez-tez o'zgaradi) va DispatchContext (dispatchuseReducer/useState setter'i hech o'zgarmaydi, barqaror). Endi faqat dispatchni ishlatgan komponent state o'zgarganda re-render bo'lmaydi (chunki u StateContextni tinglamaydi). Naqsh 2 — mavzu bo'yicha bo'lish (11.11: 2.9): auth/theme/cart'ni alohida Context'larga ajratish (cart o'zgarsa — auth/theme consumer'lari tegmaydi). Ikki nuqta: (1) state + dispatch ajratish — faqat yozuvchi komponentlar (tugma, forma) state o'zgarishiga reaksiya qilmaydi (re-render tejash); (2) mavzu bo'yicha bo'lish — mustaqil ma'lumotlarni ajratish. Ikkala naqsh Context'ning re-render cheklovini (selektor yo'q — 2.3) qisman aylanib o'tadi (lekin to'liq emas — katta state uchun baribir Redux/Zustand afzal — 2.9).

2.7. Context + useReducer — mini-Redux

text
  CONTEXT + useReducer — kichik global state (Redux'siz — 11.6 davomi):

  // reducer (sof — 11.6: 2.3):
  function cartReducer(state, action) {
    switch (action.type) {
      case "add": return [...state, action.payload];
      case "remove": return state.filter(i => i.id !== action.payload);
      case "clear": return [];
      default: return state;
    }
  }

  // Provider (useReducer + Context — state/dispatch ajratilgan — 2.6):
  function CartProvider({ children }) {
    const [cart, dispatch] = useReducer(cartReducer, []);
    return (
      <CartDispatchContext.Provider value={dispatch}>
        <CartStateContext.Provider value={cart}>{children}</CartStateContext.Provider>
      </CartDispatchContext.Provider>
    );
  }

  // Hooklar:
  const cart = useCartState();       // o'qish
  const dispatch = useCartDispatch(); // yozish
  dispatch({ type: "add", payload: product });

  ┌────────────────────────────────────────────────────────────┐
  │ Context + useReducer = "mini-Redux" (markaziy reducer +      │
  │ dispatch + Context ulashish) — kichik ilova uchun yetadi      │
  └────────────────────────────────────────────────────────────┘

   Context + useReducer — Redux'ning kichik modeli (kichik/o'rta ilova — Redux'siz)
   Redux DevTools, middleware, selektor YO'Q — katta bo'lsa Redux/Zustand (2.9)

Context + useReducer — kichik global state uchun "mini-Redux" (11.6 davomi). useReducer (markaziy state mantig'i — 11.6) va Context (ulashish) ni birlashtirib, Redux'siz global state quriladi. Tuzilishi: (1) reducer (sof funksiya — (state, action) => newState — 11.6: 2.3); (2) ProvideruseReducer bilan state/dispatch oladi va ularni Context'larga beradi (state va dispatch ajratilgan — 2.6 — performance); (3) hooklaruseCartState() (o'qish), useCartDispatch() (yozish). Ishlatishda komponentlar dispatch({ type: "add", payload }) bilan o'zgartiradi. Bu — Redux'ning kichik modeli (markaziy reducer + dispatch + Context orqali ulashish). Ikki nuqta: (1) Context + useReducer — kichik/o'rta ilova uchun yetadi (Redux o'rnatish kerak emas — React'ning o'zida); (2) lekin Redux DevTools (vaqt sayohati — kuzatish), middleware (async, logging), selektor (qism tinglash — 2.3) — bularning hech biri yo'q; ilova o'ssa va bu imkoniyatlar kerak bo'lsa — Redux Toolkit 12.2-bob yoki Zustand 12.5-bob. Bu naqsh — Context'ning state management'dagi eng kuchli ishlatilishi (oddiy global state).

2.8. Provider kompozitsiyasi — provider hell

text
  MUAMMO: ko'p Provider — ICHMA-ICH "piramida" (provider hell):
  <AuthProvider>
    <ThemeProvider>
      <CartProvider>
        <ToastProvider>
          <App />                  // 4 qatlam ichma-ich (chalkash)
        </ToastProvider>
      </CartProvider>
    </ThemeProvider>
  </AuthProvider>

  YECHIM — Provider'larni BIRLASHTIRISH (compose):
  const providers = [AuthProvider, ThemeProvider, CartProvider, ToastProvider];

  function AppProviders({ children }) {
    return providers.reduceRight(
      (acc, Provider) => <Provider>{acc}</Provider>,   // o'ngdan chapga o'rab boradi
      children
    );
  }
  // <AppProviders><App /></AppProviders>   — yassi, toza

  ┌────────────────────────────────────────────────────────────┐
  │ Provider hell: 5+ ichma-ich (chalkash)  compose (yassi)     │
  │ yoki React Context'ni mantiqiy guruhlash (auth+user birga)  │
  └────────────────────────────────────────────────────────────┘

   Ko'p Provider  compose funksiyasi (ichma-ich piramidadan qochish)
   Yoki bog'liq Context'larni birlashtirish (auth+permissions — bitta Provider)

Provider kompozitsiyasi (provider hell) — ko'p Context'ning amaliy muammosini hal qiladi. Muammo: real ilovada ko'p Provider bo'ladi (auth, theme, cart, toast, query...) — ular main.tsx/App'da ichma-ich joylansa (<AuthProvider><ThemeProvider><CartProvider>...), bu "provider hell" (piramida) — chalkash, qatlamlar ko'payganda o'qish qiyin. Yechim — Provider'larni birlashtirish (compose): providers.reduceRight((acc, Provider) => <Provider>{acc}</Provider>, children) — massivdagi Provider'larni o'ngdan chapga (reduceRight) bir-birini o'rab boradi, natijada <AppProviders><App /></AppProviders> (yassi, toza). Ikki nuqta: (1) ko'p Provider bo'lsa — compose funksiyasi (ichma-ich piramidadan qochish — toza arxitektura); (2) yoki mantiqiy bog'liq Context'larni birlashtirish (masalan auth + permissions — bitta AuthProviderda — chunki ular birga ishlatiladi). Eslatma: Provider tartibi muhim bo'lishi mumkin (masalan auth router ichida bo'lishi kerak — 11.15: Misol 14). Bu — ko'p Context'li ilovani toza tutishning amaliy usuli.

2.8.1. Context compound komponentlarda (11.13 davomi)

text
  CONTEXT'ning YANA BIR muhim ishlatilishi — COMPOUND komponent 11.13-bob:
  bog'liq komponentlar (masalan <Tabs>, <Tabs.List>, <Tabs.Panel>) o'zaro
  holatni PROPS orqali emas, ICHKI Context orqali baham ko'radi:

  <Tabs value={active} onChange={setActive}>   {/* Tabs ichida TabsContext.Provider */}
    <Tabs.List>
      <Tabs.Tab id="a">Birinchi</Tabs.Tab>     {/* ichki Context'dan active/onChange oladi */}
      <Tabs.Tab id="b">Ikkinchi</Tabs.Tab>
    </Tabs.List>
    <Tabs.Panel id="a">...</Tabs.Panel>
  </Tabs>

   Foydalanuvchi <Tabs.Tab>'ga "active"/"onChange" props uzatmaydi (ichki Context uzatadi)
   Bu "ichki" (private) Context — tashqariga export QILINMAYDI (faqat komponent oilasi ishlatadi)

   Compound komponentda Context — komponentlararo aloqa (props drilling'siz — ichki API)
   Bu Context ODATDA memo/split kerak emas (kichik, kam consumer — 11.13'da chuqur)

Context compound komponentlarda — Context'ning ilova-darajasidagi global holatdan (auth/theme) tashqari, ikkinchi muhim ishlatilishi (11.13 davomi). Compound komponent — bir-biriga bog'liq bir nechta komponentdan iborat "oila" (masalan <Tabs>, <Accordion>, <Select>); ular umumiy holatni (qaysi tab faol, qaysi element ochiq) props orqali emas, ichki Context orqali baham ko'radi. Ota-komponent (<Tabs>) o'z ichida TabsContext.Providerni joylashtiradi va holatni beradi; bola-komponentlar (<Tabs.Tab>, <Tabs.Panel>) useContext bilan uni to'g'ridan oladi — natijada foydalanuvchi bolalarga active/onChange kabi props'ni qo'lda uzatmaydi (ichki API buni yashiradi — deklarativ, toza). Ikki nuqta: (1) bu — Context'ning komponentlararo (ilova emas) ishlatilishi: bir komponent oilasi ichida props drilling'ni yo'qotadi; (2) bu Context ichki (private) — tashqariga export qilinmaydi (faqat komponent oilasi ishlatadi), va odatda kichik/kam consumer bo'lgani uchun value memo/split kabi optimizatsiyalar kritik emas (compound naqshi 11.13'da chuqur ko'rilgan). Demak, Context ikki xil miqyosda ishlaydi: global (auth/theme/i18n — butun ilova) va lokal (compound komponent — bitta oila).

2.9. Performance tuzoqlari va selektor naqshi

text
  CONTEXT PERFORMANCE TUZOQLARI va YECHIMLARI:

  1. value har render yangi  useMemo 2.4-bob
  2. bitta katta Context  bo'lish (state/dispatch, mavzu — 2.6)
  3. tez-tez o'zgaradigan katta state  Context EMAS (selektor yo'q — 2.3)

  SELEKTOR MUAMMOSI: useContext butun value'ni oladi (qismini tinglab bo'lmaydi):
  const { user } = useContext(AppContext);   // theme o'zgarsa ham BU re-render (user kerak edi)

  YECHIMLAR:
   Context'ni bo'lish (har qism alohida — 2.6)
   use-context-selector kutubxonasi (qism tinglash — Context'ga selektor qo'shadi):
    const user = useContextSelector(AppContext, (s) => s.user);   // faqat user o'zgarsa render
   Katta/murakkab state  Zustand 12.5-bob yoki Redux 12.2-bob — TABIIY selektor

  ┌────────────────────────────────────────────────────────────┐
  │ Context: selektor yo'q (butun value)  bo'lish yoki kutubxona│
  │ Zustand/Redux: tabiiy selektor (qism tinglash — afzal kattada)│
  └────────────────────────────────────────────────────────────┘

   Context'ning asosiy cheklovi — SELEKTOR yo'q (qism tinglab bo'lmaydi — 2.3)
   Katta, tez o'zgaradigan state  Zustand/Redux (selektor — kerakli render — 2.10)

Performance tuzoqlari va selektor — Context'ning cheklovlarini va yechimlarini umumlashtiradi. Uch asosiy tuzoq va yechim: (1) value har render yangi useMemo 2.4-bob; (2) bitta katta Context bo'lish (state/dispatch, mavzu — 2.6); (3) tez-tez o'zgaradigan katta state Context emas (chunki selektor yo'q — 2.3). Selektor muammosi — Context'ning tub cheklovi: useContext butun value'ni oladi, uning qismini "tinglab" bo'lmaydi (const { user } = useContext(AppContext)theme o'zgarsa ham bu komponent re-render bo'ladi, garchi faqat user kerak edi). Yechimlar: (a) Context'ni bo'l 2.6-bob; (b) use-context-selector kutubxonasi — Context'ga selektor qo'shadi (useContextSelector(AppContext, (s) => s.user) — faqat user o'zgarganda render); (c) katta/murakkab state uchun Zustand 12.5-bob yoki Redux 12.2-bob — bularda selektor tabiiy (useStore(s => s.user)). Ikki nuqta: (1) Context'ning asosiy cheklovi — selektor yo'q (qism tinglab bo'lmaydi); (2) katta, tez o'zgaradigan state uchun Zustand/Redux afzal (selektor — faqat kerakli komponent re-render). Bu — Context vs Redux/Zustand qaror 2.10-bobning asosiy dalili.

2.10. Qachon Context, qachon Redux/Zustand/Query

text
  TO'G'RI VOSITANI TANLASH (eng muhim qaror):

  ┌─────────────────────────┬──────────────────────────────────┐
  │ Ehtiyoj                 │ Vosita                            │
  ├─────────────────────────┼──────────────────────────────────┤
  │ Kam o'zgaradigan global │ Context (theme, auth, locale)     │
  │   (theme/auth/i18n)     │ (oddiy, o'rnatilgan — 2.7)        │
  │ Kichik global state     │ Context + useReducer (mini-Redux) │
  │ Katta/murakkab/tez state│ Zustand 12.5-bob yoki Redux 12.2-bob  │
  │   (selektor kerak)      │ (tabiiy selektor — 2.9)           │
  │ SERVER ma'lumoti        │ TanStack Query / RTK Query (12.3/4)│
  │   (API, kesh, sync)     │  Redux/Context EMAS (server ≠ client)│
  │ Forma holati            │ React Hook Form 11.10-bob           │
  │ URL holati (filtr)      │ useSearchParams 11.9-bob            │
  └─────────────────────────┴──────────────────────────────────┘

   ENG KENG XATO: server ma'lumotini (API javobi) Redux/Context'da saqlash
      server ma'lumoti — TanStack Query/RTK Query (kesh/sync avtomatik — 12.4)
   Client state (UI, theme, auth) ≠ Server state (API ma'lumoti) — HAR XIL vosita

Qachon Context, qachon Redux/Zustand/Query — state management'ning eng muhim qarori. To'g'ri vositani tanlash ehtiyojga bog'liq: (1) kam o'zgaradigan global (theme, auth, locale/i18n) Context (oddiy, o'rnatilgan — 2.7); (2) kichik global state Context + useReducer (mini-Redux — 2.7); (3) katta/murakkab/tez o'zgaradigan state (selektor kerak) Zustand 12.5-bob yoki Redux 12.2-bob — tabiiy selektor 2.9-bob; (4) server ma'lumoti (API javobi, kesh, sinxronlash) TanStack Query / RTK Query (12.3/12.4) — Redux/Context emas!; (5) forma holati React Hook Form 11.10-bob; (6) URL holati (filtr, sahifa) useSearchParams 11.9-bob. Eng keng xato: server ma'lumotini (API javobini) Redux yoki Context'da saqlash — bu noto'g'ri, chunki server ma'lumoti o'z muammolariga ega (kesh, fonda yangilash, eskirish, qayta urinish) va ularni TanStack/RTK Query avtomatik hal qiladi 12.4-bob; Redux/Context'ga qo'lda saqlash — ko'p qo'lda kod va xato. Client state (UI, theme, auth — brauzerda tug'iladi) va server state (API ma'lumoti — serverdan keladi) — har xil muammo, har xil vosita. Bu farqni anglash — zamonaviy frontend arxitekturasining eng muhim tushunchasidan biri (va 12-QISM'ning markaziy g'oyasi).

2.11. Server Components va Context (Next.js — qisqa)

text
  NEXT.JS (13) — Server Components 13.3-bob Context'ni TO'G'RIDAN ISHLATA OLMAYDI:

  - Server Component — server'da render (state/effect/Context yo'q — 13.3)
  - Context — CLIENT mexanizmi (brauzerda — interaktiv)
   Server Component'da useContext ISHLATIB BO'LMAYDI (xato)

  YECHIM (Next.js'da):
   Provider'ni "use client" komponentga o'rab, layout'da ishlatish:
    // providers.tsx
    "use client";
    export function Providers({ children }) { return <ThemeProvider>{children}</ThemeProvider>; }
   Context faqat CLIENT Component'larda (interaktiv qism)
   Server ma'lumoti — Context'da emas, props yoki Server Component'da (13.5)

   Context — client mexanizmi (Next.js Server Component'da ishlamaydi — "use client" kerak)
   13-QISM (Next.js)da Context'ni to'g'ri joylashtirish (client chegarasi) — chuqur

Server Components va Context — Next.js konteksti (13-QISM oldindan ko'rinishi). Next.js'ning Server Components 13.3-bob — server'da render bo'ladigan komponentlar; ularda state, effect, va Context yo'q (chunki bular client/interaktiv mexanizmlar). Shuning uchun Server Component'da useContext ishlatib bo'lmaydi (xato beradi). Yechim (Next.js'da): (1) Provider'ni "use client" direktivali komponentga o'rab, root layout'da ishlatish (Providers — barcha Context'larni client tomonda beradi); (2) Context faqat Client Component'larda ("use client" — interaktiv qism); (3) server ma'lumotini Context'da emas, props orqali yoki Server Component'ning o'zida olish (13.5 — data fetching). Ikki nuqta: (1) Context — client mexanizmi (Next.js Server Component'da ishlamaydi — "use client" chegarasi kerak); (2) 13-QISM (Next.js)da Context'ni to'g'ri joylashtirish (server/client chegarasi) chuqurroq o'rganiladi. Hozircha bilish kerak: Context — brauzer (client) tushunchasi; SSR/Server Components muhitida uni ehtiyotkorlik bilan joylashtirish kerak. Bu — Next.js'ga o'tganda muhim bo'ladigan nozik nuqta.


3. Sintaksis — tez ma'lumotnoma

text
YARATISH 2.2-bob:    const Ctx = createContext(defaultValue | null)
PROVIDER 2.2-bob:    <Ctx.Provider value={value}>{children}</Ctx.Provider>
O'QISH 2.2-bob:      const value = useContext(Ctx)
VALUE MEMO 2.4-bob:  const value = useMemo(() => ({ a, b }), [a, b])
CUSTOM HOOK 2.5-bob: function useX() { const c = useContext(Ctx); if(!c) throw...; return c }
BO'LISH 2.6-bob:     StateContext + DispatchContext (state o'qish / dispatch yozish alohida)
REDUCER 2.7-bob:     useReducer(reducer, initial) + Context'ga state/dispatch ber
COMPOSE 2.8-bob:     providers.reduceRight((acc, P) => <P>{acc}</P>, children)
SELEKTOR 2.9-bob:    useContextSelector(Ctx, s => s.user)  // use-context-selector
QAROR 2.10-bob:      kam o'zgaradiganContext | katta/tezZustand/Redux | serverQuery

4. Batafsil kod namunalari

Misol 1 — Asosiy Context (theme — 2.2)

tsx
import { createContext, useContext, useState, useMemo } from "react";

const ThemeContext = createContext<{ theme: string; toggle: () => void } | null>(null);

export function ThemeProvider({ children }: React.PropsWithChildren) {
  const [theme, setTheme] = useState("light");
  const toggle = () => setTheme((t) => (t === "light" ? "dark" : "light"));
  const value = useMemo(() => ({ theme, toggle }), [theme]);   // barqaror value (2.4)
  return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
}

export function useTheme() {
  const ctx = useContext(ThemeContext);
  if (!ctx) throw new Error("useTheme ThemeProvider ichida ishlatilishi kerak");
  return ctx;
}

// Ishlatish:
function ThemeButton() {
  const { theme, toggle } = useTheme();
  return <button onClick={toggle}>Mavzu: {theme}</button>;
}

Misol 2 — Value memo (re-render tuzog'i — 2.4)

tsx
//  Har render yangi value (consumer'lar har render render):
function BadProvider({ children }: React.PropsWithChildren) {
  const [user, setUser] = useState(null);
  return (
    <AuthContext.Provider value={{ user, setUser }}>   {/* HAR render yangi obyekt! */}
      {children}
    </AuthContext.Provider>
  );
}

//  useMemo bilan barqaror:
function GoodProvider({ children }: React.PropsWithChildren) {
  const [user, setUser] = useState(null);
  const value = useMemo(() => ({ user, setUser }), [user]);   // user o'zgargandagina
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
//  Bad: har render  consumer render; Good: user o'zgargandagina (2.4)

Misol 3 — Context bo'lish (state + dispatch — 2.6)

tsx
import { createContext, useContext, useReducer } from "react";

const CartStateContext = createContext<CartItem[] | null>(null);
const CartDispatchContext = createContext<React.Dispatch<CartAction> | null>(null);

function cartReducer(state: CartItem[], action: CartAction): CartItem[] {
  switch (action.type) {
    case "add": return [...state, action.payload];
    case "remove": return state.filter((i) => i.id !== action.payload);
    case "clear": return [];
    default: return state;
  }
}

export function CartProvider({ children }: React.PropsWithChildren) {
  const [cart, dispatch] = useReducer(cartReducer, []);
  return (
    <CartDispatchContext.Provider value={dispatch}>     {/* dispatch barqaror — re-render yo'q */}
      <CartStateContext.Provider value={cart}>{children}</CartStateContext.Provider>
    </CartDispatchContext.Provider>
  );
}

export function useCart() {
  const ctx = useContext(CartStateContext);
  if (ctx === null) throw new Error("useCart CartProvider ichida");
  return ctx;
}
export function useCartDispatch() {
  const ctx = useContext(CartDispatchContext);
  if (!ctx) throw new Error("useCartDispatch CartProvider ichida");
  return ctx;
}
//  Faqat dispatch'ni ishlatgan tugma — cart o'zgarganda RE-RENDER BO'LMAYDI (2.6)

Misol 4 — Context + useReducer ishlatish (2.7)

tsx
// Mahsulot qo'shadigan tugma — FAQAT dispatch (state o'qimaydi  cart o'zgarganda render yo'q):
function AddButton({ product }: { product: Product }) {
  const dispatch = useCartDispatch();           // faqat dispatch
  return <button onClick={() => dispatch({ type: "add", payload: product })}>Savatga</button>;
}

// Savat soni — cart'ni o'qiydi (cart o'zgarganda render — to'g'ri):
function CartBadge() {
  const cart = useCart();                        // o'qiydi
  return <span> {cart.length}</span>;
}
//  AddButton dispatch'ni, CartBadge state'ni — ajratilgan (AddButton bekorga render bo'lmaydi)

Misol 5 — Provider kompozitsiyasi (compose — 2.8)

tsx
const providers = [QueryProvider, ThemeProvider, AuthProvider, CartProvider, ToastProvider];

function AppProviders({ children }: React.PropsWithChildren) {
  return providers.reduceRight(
    (acc, Provider) => <Provider>{acc}</Provider>,   // o'ngdan chapga o'rab boradi
    children
  ) as React.ReactElement;
}

// main.tsx:
// <AppProviders><App /></AppProviders>   — ichma-ich piramida o'rniga yassi
//  5 Provider — bitta AppProviders (provider hell'dan qoch — 2.8)

Misol 6 — i18n (til) Context (real naqsh — 2.7)

tsx
type Lang = "uz" | "ru" | "en";
const translations = {
  uz: { welcome: "Xush kelibsiz", login: "Kirish" },
  en: { welcome: "Welcome", login: "Login" },
  ru: { welcome: "Dobro pojalovat", login: "Vxod" },
};

const I18nContext = createContext<{ lang: Lang; t: (key: string) => string; setLang: (l: Lang) => void } | null>(null);

export function I18nProvider({ children }: React.PropsWithChildren) {
  const [lang, setLang] = useState<Lang>("uz");
  const t = useCallback((key: string) => translations[lang][key] ?? key, [lang]);
  const value = useMemo(() => ({ lang, t, setLang }), [lang, t]);
  return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
}
export const useI18n = () => {
  const ctx = useContext(I18nContext);
  if (!ctx) throw new Error("useI18n I18nProvider ichida");
  return ctx;
};

// Ishlatish:
function Header() {
  const { t, setLang } = useI18n();
  return <h1>{t("welcome")}</h1>;   // tilga qarab matn
}

Misol 7 — Toast Context (bildirishnoma — 2.7)

tsx
interface Toast { id: string; message: string; type: "success" | "error"; }
const ToastContext = createContext<{ toasts: Toast[]; show: (m: string, t?: "success" | "error") => void } | null>(null);

export function ToastProvider({ children }: React.PropsWithChildren) {
  const [toasts, setToasts] = useState<Toast[]>([]);
  const show = useCallback((message: string, type: "success" | "error" = "success") => {
    const id = crypto.randomUUID();
    setToasts((prev) => [...prev, { id, message, type }]);
    setTimeout(() => setToasts((prev) => prev.filter((t) => t.id !== id)), 3000);   // 3s'da yo'qol
  }, []);
  const value = useMemo(() => ({ toasts, show }), [toasts, show]);
  return (
    <ToastContext.Provider value={value}>
      {children}
      <div className="toast-container">{toasts.map((t) => <div key={t.id} className={t.type}>{t.message}</div>)}</div>
    </ToastContext.Provider>
  );
}
export const useToast = () => useContext(ToastContext)!;
// Ishlatish: const { show } = useToast(); show("Saqlandi", "success");

Misol 8 — use-context-selector (qism tinglash — 2.9)

tsx
import { createContext, useContextSelector } from "use-context-selector";

const AppContext = createContext<AppState | null>(null);

// Faqat user o'zgarganda render (theme o'zgarsa — bu render BO'LMAYDI):
function UserName() {
  const user = useContextSelector(AppContext, (state) => state?.user);   // selektor
  return <span>{user?.name}</span>;
}

// Faqat theme o'zgarganda render:
function ThemeIndicator() {
  const theme = useContextSelector(AppContext, (state) => state?.theme);
  return <span>{theme}</span>;
}
//  use-context-selector — Context'ga selektor qo'shadi (qism tinglash — re-render kamayadi — 2.9)
//    Lekin: katta state uchun Zustand 12.5-bob tabiiyroq (built-in selektor)

Misol 9 — Nested Context (ichki Provider — 2.2)

tsx
const ThemeContext = createContext("light");

function App() {
  return (
    <ThemeContext.Provider value="dark">          {/* tashqi: dark */}
      <Header />                                   {/* dark */}
      <ThemeContext.Provider value="light">       {/* ichki: light (g'olib) */}
        <Sidebar />                                {/* light (eng yaqin Provider) */}
      </ThemeContext.Provider>
    </ThemeContext.Provider>
  );
}
//  useContext eng YAQIN Provider'ni oladi (Sidebar  light, Header  dark — 2.2)

Misol 10 — Context performance test (re-render ko'rsatish — 2.3)

tsx
// Bitta katta Context — har bo'lak o'zgarsa hamma render (yomon):
const BadContext = createContext<{ user: User; theme: string; cart: Item[] } | null>(null);

// Bo'lingan — har bo'lak alohida (yaxshi):
const UserContext = createContext<User | null>(null);
const ThemeContext = createContext<string>("light");
const CartContext = createContext<Item[]>([]);

function GoodProviders({ children }: React.PropsWithChildren) {
  // har bo'lak alohida Provider — biri o'zgarsa boshqasini ishlatgan komponent tegmaydi
  return (
    <UserProvider><ThemeProvider><CartProvider>{children}</CartProvider></ThemeProvider></UserProvider>
  );
}
//  Bad: cart o'zgarsa user/theme consumer ham render; Good: faqat cart consumer (2.3, 2.6)

Misol 11 — Type-safe Context factory (qayta ishlatiladigan — 11.14)

tsx
// Generic Context yaratuvchi yordamchi (null tekshiruvni bir marta yozish):
function createSafeContext<T>(name: string) {
  const Context = createContext<T | null>(null);
  function useSafeContext() {
    const ctx = useContext(Context);
    if (ctx === null) throw new Error(`use${name} must be used within ${name}Provider`);
    return ctx;
  }
  return [useSafeContext, Context.Provider] as const;
}

// Ishlatish — null tekshiruv avtomatik:
const [useAuth, AuthProviderRaw] = createSafeContext<AuthValue>("Auth");
//  Har Context uchun null tekshiruvni takrorlamaslik (DRY + type-safe — 11.14)

Misol 12 — Context vs Query (server ma'lumoti — 2.10)

tsx
//  NOTO'G'RI — server ma'lumotini Context'da saqlash (qo'lda kesh/sync — og'riq):
function BadUsersProvider({ children }: React.PropsWithChildren) {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  useEffect(() => { fetch("/api/users").then(r => r.json()).then(d => { setUsers(d); setLoading(false); }); }, []);
  //  kesh yo'q, qayta yuklash qo'lda, eskirish boshqarilmaydi (2.10)
  return <UsersContext.Provider value={{ users, loading }}>{children}</UsersContext.Provider>;
}

//  TO'G'RI — server ma'lumoti TanStack Query 12.4-bob:
function UsersList() {
  const { data: users, isLoading } = useQuery({ queryKey: ["users"], queryFn: () => fetch("/api/users").then(r => r.json()) });
  //  kesh, fonda yangilash, qayta urinish AVTOMATIK (12.4)
  if (isLoading) return <Spinner />;
  return <ul>{users.map((u: User) => <li key={u.id}>{u.name}</li>)}</ul>;
}
//  Server ma'lumoti — Query (Context emas); client state (theme/auth) — Context (2.10)

Misol 13 — Disclosure Context (modal/drawer boshqaruvi — 2.7)

tsx
// Global modal boshqaruvi (istalgan joydan ochish):
const ModalContext = createContext<{ open: (content: React.ReactNode) => void; close: () => void } | null>(null);

export function ModalProvider({ children }: React.PropsWithChildren) {
  const [content, setContent] = useState<React.ReactNode>(null);
  const open = useCallback((c: React.ReactNode) => setContent(c), []);
  const close = useCallback(() => setContent(null), []);
  const value = useMemo(() => ({ open, close }), [open, close]);   // open/close barqaror
  return (
    <ModalContext.Provider value={value}>
      {children}
      {content && <Modal onClose={close}>{content}</Modal>}        {/* 11.12 portal */}
    </ModalContext.Provider>
  );
}
export const useModal = () => useContext(ModalContext)!;
// Ishlatish: const { open } = useModal(); open(<ProductForm />);   — istalgan joydan modal

Misol 14 — To'liq auth Context (production naqsh — 2.5)

tsx
interface AuthValue {
  user: User | null;
  isLoading: boolean;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
}
const AuthContext = createContext<AuthValue | null>(null);

export function AuthProvider({ children }: React.PropsWithChildren) {
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {                                  // mount'da token'dan tikla (11.15: 2.5)
    const token = localStorage.getItem("token");
    if (!token) { setIsLoading(false); return; }
    api.get("/auth/me").then((r) => setUser(r.data)).catch(() => localStorage.removeItem("token")).finally(() => setIsLoading(false));
  }, []);

  const login = useCallback(async (email: string, password: string) => {
    const { data } = await api.post("/auth/login", { email, password });
    localStorage.setItem("token", data.token);
    setUser(data.user);
  }, []);
  const logout = useCallback(() => { localStorage.removeItem("token"); setUser(null); }, []);

  const value = useMemo(() => ({ user, isLoading, login, logout }), [user, isLoading, login, logout]);
  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth() {
  const ctx = useContext(AuthContext);
  if (!ctx) throw new Error("useAuth AuthProvider ichida ishlatilishi kerak");
  return ctx;
}
//  value memo + custom hook + persist + null tekshiruv — to'liq auth Context (2.4, 2.5)

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

1) Provider value

text
 value={{ user, setUser }}  (har render yangi  consumer render — 2.4)
 value={useMemo(() => ({ user, setUser }), [user])}

2) Context tashkili

text
 useContext(Ctx) har komponentda + null tekshiruv (takror)
 custom hook (useAuth — qulay, xavfsiz — 2.5)

3) Katta tez state

text
 tez-tez o'zgaradigan katta state Context'da (selektor yo'q — ko'p render — 2.3)
 Zustand/Redux (tabiiy selektor — 2.9, 2.10)

4) Server ma'lumoti

text
 API ma'lumotini Context/Redux'da (qo'lda kesh/sync — 2.10)
 TanStack/RTK Query (avtomatik kesh — 12.4)

5) Provider hell

text
 6 Provider ichma-ich piramida (chalkash — 2.8)
 compose funksiyasi (yassi)

6) Re-render

text
 bitta katta Context (har bo'lak hamma'ni render — 2.3)
 bo'lish (state/dispatch, mavzu — 2.6)

6. Keng tarqalgan xatolar va yechimlari

Xato 1 — Consumer har render re-render bo'ladi (sekin)

Sababi: Provider value har render yangi obyekt 2.4-bob. Yechimi: useMemo bilan value'ni barqarorlashtiring.

Xato 2 — Cannot read properties of null (useContext)

Sababi: komponent Provider tashqarisida, value null 2.5-bob. Yechimi: Provider'ni yuqorida o'rang; custom hook'da if (!ctx) throw.

Xato 3 — Context o'zgardi, lekin komponent yangilanmaydi

Sababi: value obyekti mutatsiya qilindi (yangi havola yo'q — 11.6: 2.9). Yechimi: yangi obyekt ({...prev}); state'ni to'g'ri yangilang.

Xato 4 — Bitta o'zgarish butun ilovani sekinlashtiradi

Sababi: bitta katta Context, ko'p consumer 2.3-bob. Yechimi: Context'ni bo'lish 2.6-bob, yoki selektor 2.9-bob, yoki Zustand.

Xato 5 — useContext is not a function / Server Component xatosi

Sababi: Next.js Server Component'da Context ishlatildi 2.11-bob. Yechimi: "use client" direktivasi; Provider'ni client komponentga o'rang (13).

Xato 6 — Server ma'lumoti eskirgan (Context'da)

Sababi: API ma'lumoti Context'da, sync yo'q 2.10-bob. Yechimi: TanStack Query (avtomatik refetch/kesh — 12.4).


7. Integratsiya — bu mavzu stack'ning qayerida uchraydi

  • Hooks (11.5, 11.6, 11.7): useContext, useReducer, useMemo, custom hook — Context'ning asoslari.
  • Performance 11.11-bob: Context re-render — performance muammosi (bo'lish, selektor).
  • Redux Toolkit 12.2-bob: Context + useReducer — Redux'ning kichik modeli.
  • Zustand 12.5-bob: Context muammolarini (selektor, re-render) tabiiy hal qiladi.
  • TanStack/RTK Query (12.3, 12.4): server ma'lumoti — Context emas, Query.
  • Dashboard 11.15-bob: auth/theme Context (provider tartibi).
  • Next.js 13.3-bob: Server Components va Context ("use client" chegarasi).
  • TypeScript 11.14-bob: type-safe Context (createContext<T | null>).

8. Eng yaxshi amaliyotlar (best practices)

  • value'ni useMemo (har render yangi obyekt — eng keng tuzoq — 2.4).
  • Custom hook bilan o'rash (useAuth — qulay, xavfsiz, type-safe — 2.5).
  • Context'ni bo'lish (state/dispatch, mavzu — re-render kamaytirish — 2.6).
  • Context + useReducer (kichik global state — mini-Redux — 2.7).
  • Provider'larni compose qilish (provider hell'dan qochish — 2.8).
  • Server ma'lumotini Context'da saqlamaslik (TanStack Query — 2.10).
  • Katta/tez state Zustand/Redux (selektor — 2.9, 2.10).
  • Type-safe Context (createContext<T | null> + null tekshiruv — 11.14).
  • Context obyektini export qilmaslik (faqat hook + Provider — interfeys nazorati — 2.5).
  • To'g'ri vositani tanlash (client vs server state — 2.10).

9. Amaliy loyiha: "Global Holat Tizimi (Context)"

Context'ni chuqur, to'g'ri va optimal ishlatishni mustahkamlash.

Maqsad

Ilova uchun bir nechta global Context yarat (auth, theme, i18n, toast, cart) — har biri to'g'ri optimallashtirilgan, bo'lingan, type-safe.

Talablar (requirements)

  1. Auth Context: user, login, logout, isLoading, persist (Misol 14, 2.5).
  2. Theme Context: dark/light, persist (Misol 1).
  3. i18n Context: uz/ru/en, t() funksiyasi (Misol 6).
  4. Toast Context: show(), avtomatik yo'qolish (Misol 7).
  5. Cart Context: useReducer bilan, state/dispatch ajratilgan (Misol 3, 4, 2.6).
  6. value memo: har Provider value'si barqaror (Misol 2, 2.4).
  7. Custom hook: har Context uchun useX hook + null tekshiruv (Misol 11, 2.5).
  8. Compose: Provider'larni AppProviders'da birlashtirish (Misol 5, 2.8).
  9. Selektor (bonus): kamida bitta katta Context'da use-context-selector (Misol 8, 2.9).
  10. Server ma'lumotini Context'da SAQLAMASLIK: API — Query (keyingi bob — 2.10).

Maslahatlar (hint)

  • value'ni doim useMemo bilan (Xato 1, 2.4).
  • Cart — useReducer + state/dispatch ajratish (tugma re-render bo'lmasin — 2.6).
  • Custom hook + null tekshiruv (Provider tashqarisida — aniq xato — 2.5).
  • Provider hell — compose (Misol 5, 2.8).
  • Server ma'lumoti (mahsulotlar) — Context'da emas, Query'ga qoldirish 2.10-bob.
  • React DevTools Profiler bilan re-render'ni tekshirish (bo'lish foyda berdimi — 11.11).

"Tayyor" mezonlari (acceptance criteria)

  • Auth/theme/i18n/toast/cart Context'lari ishlaydi.
  • Har value useMemo bilan barqaror.
  • Cart state/dispatch ajratilgan (dispatch komponenti re-render bo'lmaydi).
  • Har Context custom hook + null tekshiruv.
  • Provider'lar compose bilan (yassi).
  • Type-safe (createContext<T | null>).
  • Profiler — keraksiz re-render yo'q (bo'lish ishladi).
  • Server ma'lumoti Context'da saqlanmaydi (Query'ga qoldirilgan).

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


10. Xulosa va keyingi bobga ko'prik

Bu bobda Context API'ni chuqur o'rgandik:

  • Props drilling 2.1-bob; Context arxitekturasi (createContext/Provider/useContext — ichki mexanizm — 2.2); re-render xulqi (eng muhim — 2.3); value barqarorligi (memo — 2.4).
  • Custom hook 2.5-bob; Context bo'lish (state/dispatch — 2.6); Context + useReducer (mini-Redux — 2.7); provider kompozitsiyasi 2.8-bob.
  • Performance va selektor 2.9-bob; qachon Context/Redux/Zustand/Query (eng muhim qaror — 2.10); Server Components (Next.js — 2.11).

Endi siz Context'ni to'g'ri (value memo, custom hook), optimal (bo'lish, selektor), va o'rinli (qachon Context, qachon boshqa vosita) ishlata olasiz. Eng muhimi — client state vs server state farqini va vosita tanlovini tushunasiz. Bu — state management mavzusining poydevori.

Keyingi bob — 12.2-bob: Redux Toolkit (store, slice, async thunk). Context kichik global state uchun yetadi, lekin katta, murakkab ilovada (ko'p bog'liq state, murakkab yangilanish, DevTools/debugging ehtiyoji) — Redux kerak. Redux Toolkit (RTK) — Redux'ning zamonaviy, sodda usuli (eski Redux'ning boilerplate'ini yo'qotadi): store, slice (reducer + action avtomatik), Immer (mutatsiya sintaksisi, immutable natija), useSelector/useDispatch (tabiiy selektor — Context cheklovini hal qiladi), va createAsyncThunk (async — loading/error). Bu — katta ilovaning markaziy holat boshqaruvi.


Foydalanilgan rasmiy/ishonchli manbalar

  • React rasmiy hujjati (react.dev):
    • useContext — API ma'lumotnomasi (Provider, default qiymat, consumer xulqi).
    • createContext — kontekst yaratish va TypeScript bilan tiplash.
    • "Passing Data Deeply with Context" — props drilling muammosi va Context yechimi.
    • "Scaling Up with Reducer and Context" — Context + useReducer (mini-global-store) naqshi.
    • "Managing State" bo'limi — qachon lokal state, qachon Context, holatni ko'tarish (lifting state up).
  • use-context-selector — Context'ga selektor qo'shuvchi kutubxona rasmiy hujjati (qism tinglash, re-render kamaytirish).
  • TanStack Query rasmiy hujjati — client state va server state farqi (server ma'lumotini Context'da saqlamaslik sababi — 12.4 bilan bog'liq).
  • TypeScript + React rasmiy tavsiyalari — createContext<T | null> va type-safe custom hook naqshi.

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
12.1-bob: Context API (chuqur) — Wisar