IlmHamroh
JavaScript Full-stack/17-qism. React asoslari41/45-dars20 daqiqa
Mundarija (27)

React Context I: createContext, use va props drilling'siz ma'lumot uzatish

Qisqacha: Ma'lumot ko'p qavat pastdagi komponentga kerak bo'lsa, uni har oraliq komponent orqali props bilan tashish — props drilling. Context buni hal qiladi: const Lang = createContext("uz") yarating, daraxtning yuqorisida <Lang value="en"> bilan bering, kerakli joyda useContext(Lang) yoki use(Lang) bilan oling. Oraliq komponentlar bu qiymat haqida hech narsa bilmaydi. Provider bo'lmasa — default qiymat, bir nechta bo'lsa — eng yaqini. Context — global o'zgaruvchi emas: qiymat o'zgarsa, uni o'qiyotgan komponentlar qayta chiziladi.

Bu darsda

  • Props drilling nima va u qachon muammoga aylanishini ko'rsata olasiz.
  • createContext, <Context value> va useContext/use bilan qiymat uzatasiz.
  • Default qiymat va ichma-ich Provider'lar qanday ishlashini tushuntirasiz.
  • use(Context) ning useContext dan farqini bilasiz.
  • Context o'rniga props yoki kompozitsiya yetarli bo'lgan holatlarni ajratasiz.

Oldin bilishingiz kerak: Props, children va kompozitsiya, State'ni ko'tarish, Hooklar qoidalari.

1. Nega bu kerak?

«Bahor» saytiga sayyohlar ham kiradi, shuning uchun menyu ikki tilda: o'zbekcha va inglizcha. Tanlangan til App da saqlanadi. Lekin uni ishlatadigan joy — eng pastdagi Price komponenti ("Narx" yoki "Price" deb yozadi). Oraliqda esa MenuPage, MenuList, DishCard bor.

Sardor lang ni props bilan tushirdi: App → MenuPage → MenuList → DishCard → Price. To'rt qavat. MenuPage, MenuList va DishCard tilni o'zi ishlatmaydi, faqat pastga uzatadi. Keyin valyuta ham qo'shildi ("so'm" / "UZS") — yana to'rt joyga yangi prop. Bu holat props drilling ("props'ni burg'ulab tushirish") deb ataladi.

Hayotiy o'xshatish: oshxonadagi radio. Oshpaz ofitsiantlarga xabarni og'izdan-og'izga uzatmaydi ("Aliga ayt, u Valiga aytsin…"). U radio orqali e'lon qiladi — eshitishi kerak bo'lgan har kim to'lqinga ulanib eshitadi, oradagilar esa xabarni tashib yurmaydi. Context — shunday to'lqin.

2. Props drilling — muammoni ko'ramiz

jsx
import { useState } from "react";

function Price({ lang, amount }) {
  return <b>{lang === "uz" ? "Narx" : "Price"}: {amount}</b>;
}

function DishCard({ lang, dish }) {
  return (
    <li>
      {dish.name} — <Price lang={lang} amount={dish.price} />
    </li>
  );
}

function MenuList({ lang, dishes }) {
  return (
    <ul>
      {dishes.map((d) => (
        <DishCard key={d.id} lang={lang} dish={d} />
      ))}
    </ul>
  );
}

function MenuPage({ lang }) {
  const dishes = [
    { id: 1, name: "Osh", price: 35000 },
    { id: 2, name: "Manti", price: 30000 },
  ];
  return <MenuList lang={lang} dishes={dishes} />;
}

function App() {
  const [lang, setLang] = useState("uz");
  return (
    <div>
      <button onClick={() => setLang("en")}>English</button>
      <MenuPage lang={lang} />
    </div>
  );
}

Ekranda:

text
English
Osh — Price: 35000
Manti — Price: 30000

Ishlaydi. Lekin lang beshta joyda yozilgan, to'rtta komponent imzosida turibdi, ulardan uchtasi uni ishlatmaydi. Muammolar:

  • O'zgartirish qimmat. Yangi umumiy qiymat (valyuta, tema, foydalanuvchi) — yana hamma oraliq komponentni tahrirlash.
  • Oraliq komponentlar "ifloslanadi". MenuList ning vazifasi — ro'yxat chizish. Tilga uning aloqasi yo'q, lekin imzosida lang bor.
  • Qayta ishlatish qiyinlashadi. DishCard ni boshqa sahifaga olib borsangiz, u yerda ham lang ni tashib kelish kerak.
flowchart TD
  A["App: lang"] -->|lang| B["MenuPage"]
  B -->|lang| C["MenuList"]
  C -->|lang| D["DishCard"]
  D -->|lang| E["Price — ishlatadi"]
  A2["App: Lang value"] -.->|"Context to'lqini"| E2["Price — use(Lang)"]
  A2 --> B2["MenuPage"] --> C2["MenuList"] --> D2["DishCard"] --> E2

Chap tomonda — props drilling: har strelka bitta lang prop'i. O'ng tomonda — Context: oraliq komponentlar oddiy, qiymat punktir bo'ylab to'g'ridan-to'g'ri pastga yetadi.

Tekshirib ko'ring: Props drilling har doim yomonmi? App → Header → Logo — ikki qavat.

Javob

Yo'q. Ikki-uch qavatli aniq props — eng tushunarli yo'l: kodni o'qigan odam ma'lumot qayerdan kelganini darhol ko'radi. react.dev ham avval props bilan boshlashni maslahat beradi. Muammo — chuqur va ko'p joyda takrorlanadigan uzatish.

3. Context: uch qadam

3.1 Yaratish, berish, olish

jsx
import { createContext, useContext, useState } from "react";

// 1) Yaratish — default qiymat bilan
const LangContext = createContext("uz");

function Price({ amount }) {
  const lang = useContext(LangContext); // 3) Olish
  return <b>{lang === "uz" ? "Narx" : "Price"}: {amount}</b>;
}

function DishCard({ dish }) {
  return <li>{dish.name} — <Price amount={dish.price} /></li>;
}

function MenuList({ dishes }) {
  return (
    <ul>
      {dishes.map((d) => <DishCard key={d.id} dish={d} />)}
    </ul>
  );
}

function MenuPage() {
  const dishes = [
    { id: 1, name: "Osh", price: 35000 },
    { id: 2, name: "Manti", price: 30000 },
  ];
  return <MenuList dishes={dishes} />;
}

function App() {
  const [lang, setLang] = useState("uz");
  return (
    <LangContext value={lang}> {/* 2) Berish */}
      <button onClick={() => setLang("en")}>English</button>
      <MenuPage />
    </LangContext>
  );
}

Ekranda:

text
English
Osh — Price: 35000
Manti — Price: 30000

Natija bir xil, lekin MenuPage, MenuList, DishCard dan lang yo'qoldi. Uchta qadam:

  1. createContext(default) — modul darajasida, komponentdan tashqarida. Natija — kontekst obyekti, uni eksport qilib boshqa fayllarda ishlatasiz.
  2. <LangContext value={…}> — Provider (ta'minlovchi). U o'z ichidagi butun daraxtga qiymat beradi. React 19 dan kontekstning o'zi Provider bo'la oladi.
  3. useContext(LangContext) — eng yaqin yuqoridagi Provider qiymatini qaytaradi.

3.2 Qiymat o'zgarsa

Provider qiymati — oddiy state (lang). «English» bosilganda App qayta chiziladi, Provider yangi qiymat beradi va uni o'qiyotgan Price lar ham yangilanadi.

Qadam lang (App state'i) useContext natijasi Ekranda
boshida "uz" "uz" Narx: 35000
«English» "en" "en" Price: 35000

Bu — global o'zgaruvchidan asosiy farq. window.lang = "en" deb yozsangiz, React buni sezmaydi va hech narsa qayta chizilmaydi. Context esa React'ning o'z mexanizmi: qiymat o'zgarsa, iste'molchilar (consumer — qiymatni o'qiydigan komponentlar) qayta chiziladi. Buning narxi va chegaralari — Context II darsida.

3.3 Pastdan o'zgartirish: qiymat va funksiya birga

Til tugmasi ham chuqurda bo'lsa-chi? Masalan, sahifa pastidagi Footer ichida. setLang ni yana props bilan tushirish — o'sha drilling. Yechim: Provider'ga obyekt beriladi — qiymat va uni o'zgartiruvchi funksiya birga:

jsx
import { createContext, use, useState } from "react";

const LangContext = createContext({ lang: "uz", setLang: () => {} });

function LangSwitcher() {
  const { lang, setLang } = use(LangContext);
  const next = lang === "uz" ? "en" : "uz";
  return (
    <button onClick={() => setLang(next)}>
      {next === "en" ? "English" : "O'zbekcha"}
    </button>
  );
}

function Footer() {
  return <footer><LangSwitcher /></footer>;
}

function Greeting() {
  const { lang } = use(LangContext);
  return <h2>{lang === "uz" ? "Xush kelibsiz!" : "Welcome!"}</h2>;
}

function App() {
  const [lang, setLang] = useState("uz");
  return (
    <LangContext value={{ lang, setLang }}>
      <Greeting />
      <Footer />
    </LangContext>
  );
}

Ekranda:

text
Welcome!
O'zbekcha

Holat hali ham App da — "yagona haqiqat manbai" (State'ni ko'tarish) saqlandi. Context faqat yetkazib berish yo'li: LangSwitcher setLang ni chaqiradi, App ning state'i o'zgaradi, Provider yangi obyekt beradi va ikkala iste'molchi qayta chiziladi.

Qadam App dagi lang Greeting LangSwitcher
boshida "uz" Xush kelibsiz! English
«English» (pastdan) "en" Welcome! O'zbekcha

Default qiymatdagi setLang: () => {} — "hech narsa qilmaydigan" funksiya: Provider'siz chizilgan LangSwitcher yiqilmaydi, faqat ishlamaydi. Bu ma'lum bir xatoni yashirib qo'yadi — Provider'ni unutganingizni sezmaysiz. Keyingi darsda buning yaxshiroq yo'lini ko'ramiz.

Tekshirib ko'ring: Nega setLang ni Provider'ga bermasdan, LangSwitcher ning o'zida useState yozib qo'ya qolmaymiz?

Javob

Unda LangSwitcher ning o'z lang i bo'lardi — App dagisidan alohida. Tugma bosilganda faqat u o'zgarardi, Greeting eski tilda qolardi. State ikki komponentga umumiy bo'lsa, u ularning umumiy otasida turadi; Context esa faqat bu state'ni pastga yetkazadi.

3.4 TypeScript: kontekst turi

TypeScript kontekst turini default qiymatdan oladi. Ruxsat etilgan qiymatlar aniq bo'lsa, turni o'zingiz yozing:

tsx
import { createContext } from "react";

type Lang = "uz" | "en";
const LangContext = createContext<Lang>("uz");

export default function App() {
  return (
    <LangContext value="ru">
      <p>Menyu</p>
    </LangContext>
  );
}

Xato:

text
TS2322: Type '"ru"' is not assignable to type 'Lang'.

""ru" turi Lang ga mos emas." Provider'ga noto'g'ri qiymat berish kompilyatsiyada ushlandi. createContext<Lang> — generik (Generics): kontekst qiymatining turi burchakli qavsda.

4. Default qiymat va eng yaqin Provider

4.1 Provider yo'q bo'lsa

createContext ga berilgan qiymat — "zaxira": yuqorida hech qanday Provider bo'lmasa, useContext shuni qaytaradi.

jsx
import { createContext, useContext } from "react";

const ThemeContext = createContext("yorug'");

function Panel() {
  const theme = useContext(ThemeContext);
  return <p>Tema: {theme}</p>;
}

function App() {
  return (
    <div>
      <Panel />
      <ThemeContext value="qorong'i">
        <Panel />
      </ThemeContext>
    </div>
  );
}

Ekranda:

text
Tema: yorug'
Tema: qorong'i

Birinchi Panel Provider tashqarisida — default. Ikkinchisi ichida — Provider qiymati. Default qiymat Provider'ning "boshlang'ich qiymati" emas. U faqat Provider umuman yo'q bo'lganda ishlaydi. Provider bor, lekin value unutilgan bo'lsa — boshqa narsa:

jsx
import { createContext, useContext } from "react";

const ThemeContext = createContext("yorug'");

function Panel() {
  return <p>Tema: {useContext(ThemeContext)}</p>;
}

function App() {
  return (
    <ThemeContext>
      <Panel />
    </ThemeContext>
  );
}

Konsolda:

text
The `value` prop is required for the `<Context.Provider>`. Did you misspell it or forget to pass it?

Ekranda:

text
Tema:

"Provider uchun value prop'i majburiy. Xato yozdingizmi yoki unutdingizmi?" Natija — undefined, default emas: Provider bor, demak uning qiymati (undefined) ishlatiladi. valeu kabi xato yozilganda ham xuddi shu ogohlantirish chiqadi.

4.2 Ichma-ich Provider'lar

Bir xil kontekstning bir nechta Provider'i bo'lishi mumkin. Har iste'molchi eng yaqin yuqoridagini oladi:

jsx
import { createContext, useContext } from "react";

const BranchContext = createContext("bosh ofis");

function Address() {
  return <li>Filial: {useContext(BranchContext)}</li>;
}

function App() {
  return (
    <ul>
      <Address />
      <BranchContext value="Chilonzor">
        <Address />
        <BranchContext value="Yunusobod">
          <Address />
        </BranchContext>
        <Address />
      </BranchContext>
    </ul>
  );
}

Ekranda:

text
Filial: bosh ofis
Filial: Chilonzor
Filial: Yunusobod
Filial: Chilonzor

To'rtinchi Address yana "Chilonzor" ni oldi — ichki Provider faqat o'z bolalariga ta'sir qiladi. Bu xuddi CSS'dagi meros (Meros): eng yaqin ota qiymati ustun. Amalda shunday ishlatiladi: sahifaning bir qismini boshqa til yoki tema bilan ko'rsatish, test'da komponentni soxta qiymat bilan o'rash.

Tekshirib ko'ring: Komponent o'zi chizgan Provider'ning qiymatini o'qiy oladimi? Masalan, App ichida <Lang value="en"> va shu App ning o'zida useContext(Lang).

Javob

Yo'q — App dagi useContext(Lang) App dan yuqoridagi Provider'ni qidiradi. O'zi qaytargan <Lang value="en"> faqat bolalariga ta'sir qiladi. App uchun bu "en" emas, default yoki undan yuqoridagi qiymat.

5. use(Context) — React 19

React 19 da kontekstni o'qishning ikkinchi usuli paydo bo'ldi: use. Farqi — uni shart ichida, erta return dan keyin ham chaqirish mumkin. useContext da bunday qilib bo'lmaydi:

jsx
function Price({ show }) {
  if (!show) return null;
  const lang = useContext(Lang); // ❌ erta return'dan keyin
  return <p>{lang}</p>;
}

ESLint (eslint-plugin-react-hooks 7.1.1) buni darhol ushlaydi:

text
  7:16  error  React Hook "useContext" is called conditionally. React Hooks must be called in the exact same order in every component render. Did you accidentally call a React Hook after an early return?  react-hooks/rules-of-hooks

"useContext shartli chaqirilyapti. Hooklar har renderda aynan bir xil tartibda chaqirilishi kerak. Erta return dan keyin chaqirib qo'ymadingizmi?" — Hooklar qoidalari. use esa oddiy hook emas, bu qoidadan ozod:

jsx
import { createContext, use, useState } from "react";

const LangContext = createContext("uz");

function Price({ show }) {
  if (!show) return <p>Narx yashirin</p>;
  const lang = use(LangContext); // ✅ shartdan keyin ham mumkin
  return <p>{lang === "uz" ? "Narx" : "Price"}: 35000</p>;
}

function App() {
  const [show, setShow] = useState(false);
  return (
    <LangContext value="en">
      <Price show={show} />
      <button onClick={() => setShow(true)}>Ko'rsatish</button>
    </LangContext>
  );
}

Ekranda:

text
Price: 35000
Ko'rsatish

use kontekstdan tashqari Promise'ni ham o'qiy oladi — bu imkoniyatni use(): promise va context darsida (18-qism) ko'ramiz. Yangi kodda qaysi birini tanlash — did masalasi: useContext hali ham to'liq qo'llab-quvvatlanadi, use esa qulayroq. vazifalar kanonida use ishlatilgan.

6. Eski yozuvlar: .Provider va .Consumer

React 19 gacha Provider faqat .Provider bilan yozilardi. Eski kodda va kutubxonalarda buni ko'p uchratasiz — u hozir ham ishlaydi:

jsx
import { createContext } from "react";

const LangContext = createContext("uz");

function App() {
  return (
    <LangContext.Provider value="en">
      <LangContext.Consumer>
        {(lang) => <p>Til: {lang}</p>}
      </LangContext.Consumer>
    </LangContext.Provider>
  );
}

Ekranda:

text
Til: en

.Consumer — hooklardan oldingi usul: bolasi funksiya bo'ladi, qiymat uning argumenti ("render props", HOC va render props). React 19 e'lonida (2024-12-05) aytilgan: <Context> endi Provider sifatida ishlaydi, .Provider esa kelajakda eskirgan deb belgilanadi. Yangi kodda <LangContext value> va use/useContext yozing.

7. Qachon Context kerak emas

react.dev Context'dan oldin ikki yo'lni sinashni maslahat beradi.

1. Props bilan boshlang. O'nta props ham bir necha qavatda — normal. Aniq props — ma'lumot oqimini ko'rsatadi.

2. Kompozitsiya — JSX'ni children qilib bering (children va kompozitsiya). Ko'p hollarda drilling sababi — oraliq komponent o'zi chizishi kerak bo'lmagan narsani chizyapti. Masalan, Layout ga user ni faqat Avatar ga uzatish uchun beryapsiz. Buning o'rniga Avatar ni tayyor holda bering:

jsx
function Avatar({ user }) {
  return <b>{user.name}</b>;
}

function Layout({ header, children }) {
  return (
    <section>
      <header>{header}</header>
      <main>{children}</main>
    </section>
  );
}

function App() {
  const user = { name: "Jasur aka" };
  return (
    <Layout header={<Avatar user={user} />}>
      <p>Bugungi buyurtmalar: 12</p>
    </Layout>
  );
}

Ekranda:

text
Jasur aka
Bugungi buyurtmalar: 12

Layout user haqida hech narsa bilmaydi — u faqat joy ajratadi. user App dan Avatar ga to'g'ridan-to'g'ri ketdi, Context'siz.

Vaziyat Yechim
1–3 qavat, bir nechta komponent props
oraliq komponent faqat "joy" beradi kompozitsiya: children, header={<…/>}
ko'p joyda kerak bo'ladigan "muhit" qiymati: til, tema, joriy foydalanuvchi, ruxsatlar Context
tez-tez o'zgaradigan katta ma'lumot (ro'yxat, sichqoncha joylashuvi) ehtiyot bo'ling — Context II

8. Vazifalar qadami: qator hodisalari — Context orqali (1-qism)

8.1 Drilling vazifalar da

Hodisalar darsidan beri ro'yxat qatorining tugmalari otaga xabar beradi: onAlmashtir, onOchir, onTahrirla, onSaqla, onBekor va fokus uchun tugmaRef. Ular bitta QatorAmallari obyektida, va bu obyekt uch qavat pastga tushadi:

text
App — amallar={{ onAlmashtir, onOchir, … }}
└── VazifaRoyxati — amallar={amallar}          (o'zi ishlatmaydi)
    └── VazifaQatori — {...amallar}            (o'zi ishlatmaydi)
        ├── OddiyQator — onAlmashtir, onOchir, onTahrirla, tugmaRef
        └── TahrirQatori — onSaqla, onBekor

VazifaRoyxati va VazifaQatori ning vazifasi — ro'yxatni va qator turini tanlash. Hodisalar ularga kerak emas. Bu — darslik misolidagi props drilling. Yangi branch: refactor/kontekst.

8.2 Kontekst fayli

src/kontekst/qator-amallari.ts:

ts
// qator-amallari.ts — ro'yxat qatorlari hodisalari Context orqali
// (17/#41). #11–#39 da QatorAmallari obyekti props bilan uch
// qavat pastga tushardi: App → VazifaRoyxati → VazifaQatori →
// OddiyQator. O'rtadagi ikkitasiga u kerak emas edi (props
// drilling) — endi qator o'zi oladi
import { createContext, use } from "react";
import type { QatorAmallari } from "../turlar.ts";

export const QatorAmallariKonteksti =
  createContext<QatorAmallari | null>(null);

export function useQatorAmallari(): QatorAmallari {
  const amallar = use(QatorAmallariKonteksti);
  if (amallar === null) {
    throw new Error(
      "useQatorAmallari() — <QatorAmallariKonteksti value> ichida",
    );
  }
  return amallar;
}

Izoh qatorlari telefonga sig'ishi uchun qayta bo'lingan. Ikki yangilik bor. Default qiymat — null: hodisalarning "zaxira" varianti ma'nosiz, Provider'siz qator ishlamasligi kerak. useQatorAmallari — kontekstni o'qib, null ni tekshiradigan kichik funksiya. Bu naqsh (custom hook + null tekshiruv) va uning foydasini keyingi darsda ochamiz.

8.3 Komponentlar

App.tsx — VazifaRoyxati endi Provider ichida, amallar prop'i yo'q:

diff
       <ul ref={royxatRef} id="royxat" aria-labelledby={sarlavhaId}>
-        <VazifaRoyxati
-          vazifalar={korinadigan}
-          sorov={qidiruv}
-          tahrirId={tahrirId}
-          amallar={{
+        {/* Qator hodisalari — Context (17/#41): VazifaRoyxati va
+            VazifaQatori ularni endi tashib o'tmaydi */}
+        <QatorAmallariKonteksti
+          value={{
             onAlmashtir: katakniAlmashtir,
             …
             tugmaRef,
           }}
-        />
+        >
+          <VazifaRoyxati
+            vazifalar={korinadigan}
+            sorov={qidiruv}
+            tahrirId={tahrirId}
+          />
+        </QatorAmallariKonteksti>
       </ul>

VazifaQatori.tsx — imzo qisqardi:

diff
-export function VazifaQatori({
-  vazifa,
-  sorov,
-  tahrirda,
-  amallar,
-}: Props) {
-  // {...amallar} — obyektdagi har bir funksiya alohida prop bo'lib
-  // o'tadi; qator faqat o'ziga keraklisini oladi (17/#06: spread)
+export function VazifaQatori({ vazifa, sorov, tahrirda }: Props) {
+  // Hodisalar (onAlmashtir...) — props'da emas: qatorning o'zi
+  // kontekstdan oladi (17/#41)
   if (tahrirda) {
-    return <TahrirQatori vazifa={vazifa} {...amallar} />;
+    return <TahrirQatori vazifa={vazifa} />;
   }
-  return <OddiyQator vazifa={vazifa} sorov={sorov} {...amallar} />;
+  return <OddiyQator vazifa={vazifa} sorov={sorov} />;
 }

VazifaRoyxati.tsx dan ham amallar olib tashlandi. Hodisa kerak bo'lgan ikki komponent esa o'zi oladi — OddiyQator.tsx:

diff
-export function OddiyQator({
-  vazifa,
-  sorov,
-  onAlmashtir,
-  onOchir,
-  onTahrirla,
-  tugmaRef,
-}: Props) {
+export function OddiyQator({ vazifa, sorov }: Props) {
+  // Hodisalar — kontekstdan (17/#41), props zanjirisiz
+  const { onAlmashtir, onOchir, onTahrirla, tugmaRef } =
+    useQatorAmallari();

TahrirQatori.tsx da ham xuddi shunday: const { onSaqla, onBekor } = useQatorAmallari();. Props turlari soddalashdi: Pick<QatorAmallari, …> & {…} o'rniga oddiy { vazifa; sorov }.

8.4 Hozircha — commit'siz

Testlar ham o'zgaradi. VazifaRoyxati.test.tsx endi ro'yxatni Provider bilan o'raydi — xuddi App dagidek:

tsx
  // Hodisalar — Context orqali (17/#41), App'dagidek
  return render(
    <QatorAmallariKonteksti value={amallar}>
      <ul>
        <VazifaRoyxati
          vazifalar={vazifalar}
          sorov={sorov}
          tahrirId={tahrirId}
        />
      </ul>
    </QatorAmallariKonteksti>,
  );

Bu — ichma-ich Provider'ning amaliy foydasi: test komponentga soxta (vi.fn()) hodisalarni Context orqali beradi. Commit hali qilinmaydi: shu branch'da keyingi darsda tema ham Context'ga o'tadi va ikkalasi bitta commit bo'ladi.

9. Ko'p uchraydigan xatolar

Belgi Sabab Davosi
har doim default qiymat iste'molchi Provider ichida emas Provider'ni daraxtda yuqoriroqqa qo'ying
The \value` prop is required for the `<Context.Provider>`…` value unutilgan yoki xato yozilgan <Ctx value={…}>
React Hook "useContext" is called conditionally… useContext erta return dan keyin tepaga ko'chiring yoki use
window.x = … dan keyin ekran o'zgarmadi global o'zgaruvchi — React bilmaydi Context + state
komponent o'zi bergan qiymatni o'qimaydi useContext yuqoridagi Provider'ni qidiradi qiymatni o'zgaruvchidan to'g'ridan-to'g'ri oling
ikki xil createContext (nusxa) kontekst har fayl/renderda qayta yaratilgan modul darajasida bitta, eksport qiling

Oxirgi qator haqida: createContext ni komponent ichida chaqirsangiz, har renderda yangi kontekst yaratiladi va Provider bilan iste'molchi "boshqa-boshqa radio to'lqinida" bo'lib qoladi.

10. Mashqlar

Mashqlar kurs/mashqlar/17/41-context/ da.

1-mashq (oson): valyuta konteksti

Bo'sh joylarni to'ldiring:

jsx
const CurrencyContext = [___:createContext]("so'm");

function Price({ amount }) {
  const currency = [___:useContext](CurrencyContext);
  return <b>{amount} {currency}</b>;
}

function App() {
  return (
    <CurrencyContext [___:value]="UZS">
      <Price amount={35000} />
    </CurrencyContext>
  );
}
Yechim
jsx
import { createContext, useContext } from "react";

const CurrencyContext = createContext("so'm");

function Price({ amount }) {
  const currency = useContext(CurrencyContext);
  return <b>{amount} {currency}</b>;
}

function App() {
  return (
    <p>
      <CurrencyContext value="UZS">
        <Price amount={35000} />
      </CurrencyContext>{" "}
      | <Price amount={28000} />
    </p>
  );
}

Ekranda:

text
35000 UZS | 28000 so'm

Ikkinchi Price Provider tashqarisida — default "so'm".

2-mashq (o'rta): drilling'ni olib tashlang

Quyidagi kodda discount (chegirma foizi) uch qavat pastga tushyapti. TypeScript'da DiscountContext yarating va oraliq komponentlardan discount ni olib tashlang. Chegirmani App dagi tugma o'zgartiradi. Narx hisobi: price * (100 - discount) / 100.

tsx
import { useState } from "react";

type Dish = { id: number; name: string; price: number };

type PriceProps = { price: number; discount: number };
function Price({ price, discount }: PriceProps) {
  return <b>{(price * (100 - discount)) / 100}</b>;
}

type RowProps = { dish: Dish; discount: number };
function DishRow({ dish, discount }: RowProps) {
  return (
    <li>
      {dish.name}: <Price price={dish.price} discount={discount} />
    </li>
  );
}

type MenuProps = { dishes: Dish[]; discount: number };
function Menu({ dishes, discount }: MenuProps) {
  return (
    <ul>
      {dishes.map((d) => (
        <DishRow key={d.id} dish={d} discount={discount} />
      ))}
    </ul>
  );
}

export default function App() {
  const [discount, setDiscount] = useState(0);
  const dishes = [{ id: 1, name: "Lag'mon", price: 28000 }];
  return (
    <div>
      <button onClick={() => setDiscount(10)}>Chegirma 10%</button>
      <Menu dishes={dishes} discount={discount} />
    </div>
  );
}

Ekranda:

text
Chegirma 10%
Lag'mon: 25200
Yechim
tsx
import { createContext, use, useState } from "react";

type Dish = { id: number; name: string; price: number };

const DiscountContext = createContext(0);

function Price({ price }: { price: number }) {
  const discount = use(DiscountContext);
  return <b>{(price * (100 - discount)) / 100}</b>;
}

function DishRow({ dish }: { dish: Dish }) {
  return <li>{dish.name}: <Price price={dish.price} /></li>;
}

function Menu({ dishes }: { dishes: Dish[] }) {
  return (
    <ul>
      {dishes.map((d) => <DishRow key={d.id} dish={d} />)}
    </ul>
  );
}

export default function App() {
  const [discount, setDiscount] = useState(0);
  const dishes = [{ id: 1, name: "Lag'mon", price: 28000 }];
  return (
    <DiscountContext value={discount}>
      <button onClick={() => setDiscount(10)}>Chegirma 10%</button>
      <Menu dishes={dishes} />
    </DiscountContext>
  );
}

Ekranda:

text
Chegirma 10%
Lag'mon: 25200

createContext(0) — TypeScript turini default qiymatdan oladi: Context<number>. Menu va DishRow endi faqat o'z ishini qiladi.

3-mashq (qiyin): filial narxlari

Har filialda narxlar o'zgacha bo'lishi mumkin. PriceListContext yarating: qiymati — Record<string, number> (taom nomi → narx). Default — umumiy narxlar: osh 35 000, manti 30 000. Yunusobod filialida osh 37 000 (manti — umumiy). Sahifada ikki blok: «Chilonzor» (Provider'siz — umumiy narxlar) va «Yunusobod» (o'z Provider'i bilan). Yunusobod Provider'i umumiy ro'yxatni ustidan yozsin — faqat farq qiladigan narxni bering.

Ishora: Provider ichida yuqoridagi qiymatni o'qib, ustiga yozish mumkin: { ...use(PriceListContext), Osh: 37000 } — buni alohida komponentda qiling.

Yechim
tsx
import { createContext, use } from "react";
import type { ReactNode } from "react";

type PriceList = Record<string, number>;

const PriceListContext = createContext<PriceList>({
  Osh: 35000,
  Manti: 30000,
});

function PriceOverride({
  prices,
  children,
}: {
  prices: PriceList;
  children: ReactNode;
}) {
  const parent = use(PriceListContext); // yuqoridagi ro'yxat
  return (
    <PriceListContext value={{ ...parent, ...prices }}>
      {children}
    </PriceListContext>
  );
}

function Branch({ name }: { name: string }) {
  const prices = use(PriceListContext);
  return (
    <li>
      {name}: osh {prices.Osh}, manti {prices.Manti}
    </li>
  );
}

export default function App() {
  return (
    <ul>
      <Branch name="Chilonzor" />
      <PriceOverride prices={{ Osh: 37000 }}>
        <Branch name="Yunusobod" />
      </PriceOverride>
    </ul>
  );
}

Ekranda:

text
Chilonzor: osh 35000, manti 30000
Yunusobod: osh 37000, manti 30000

PriceOverride — o'zi ham iste'molchi (yuqoridagi qiymatni o'qiydi), o'zi ham Provider (pastga yangisini beradi). Ichma-ich Provider'lar shu tarzda "meros + ustidan yozish" ni beradi. Mavzular (theme) tizimlarida xuddi shu naqsh bilan sahifaning bir qismi boshqa rangda chiziladi.

11. Real ishda

  • Tema, til, joriy foydalanuvchi, ruxsatlar — Context'ning klassik ishlatilishi. Kutubxonalar ham uni ishlatadi: router joriy manzilni, forma kutubxonasi forma holatini, UI kutubxonasi temani Context orqali beradi.
  • Test'da komponentni kerakli Provider bilan o'rab, soxta qiymat berish — kundalik ish (vazifalar dagi VazifaRoyxati.test.tsx kabi).
  • Kod ko'rigida ikki xil izoh uchraydi: "bu props drilling — Context'ga chiqaring" va aksincha "bu qiymat faqat bitta joyda kerak — Context ortiqcha, props yetadi".
  • Intervyu savollari: "Props drilling nima va uni qanday hal qilasiz?", "Default qiymat qachon ishlaydi?", "use va useContext farqi?", "Context'ning muqobillari qanday?".

Xulosa

  • Props drilling — qiymatni ishlatmaydigan oraliq komponentlar orqali tashish; chuqur bo'lsa, o'zgartirish qimmat.
  • Context: createContext(default) → <Ctx value> → useContext(Ctx) yoki use(Ctx).
  • Provider yo'q — default; bir nechta — eng yaqini; value unutilsa — undefined va ogohlantirish.
  • Provider qiymati state bo'lsa, o'zgarganda iste'molchilar qayta chiziladi — global o'zgaruvchidan farqi shu.
  • use shartdan keyin ham chaqiriladi; .Provider/.Consumer — eski yozuv.
  • Avval props va kompozitsiya; Context — ko'p joyga kerak "muhit" qiymatlari uchun.

Keyingi dars: Context II: reducer bilan birga, performance va chegaralar — Context + useReducer bilan kichik global holat yasaymiz, qayta chizish narxini o'lchaymiz va vazifalar dagi temani Context'ga o'tkazamiz.

Manbalar

  • React: "Passing Data Deeply with Context" — react.dev/learn/passing-data-deeply-with-context
  • React: createContext, useContext, use — react.dev/reference/react
  • React 19 e'loni: "<Context> as a provider" (2024-12-05) — react.dev/blog/2024/12/05/react-19
Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
React Context I: createContext, use va props drilling'siz ma'lumot uzatish — IlmHamroh