useContext — Context nima, Provider, Consumer

~20 daq · React · Hooks — Ilg'or

Muammo: prop drilling

7-darsda prop drilling haqida gapirdik. Keling, muammoni ko'ramiz.

Tasavvur qiling: foydalanuvchi login qildi. Uning ismi navbar da, profil sahifasida, sozlamalar sahifasida ko'rsatilishi kerak. Bu ma'lumot App komponentida bor.

App  (user = { ism: 'Jasur' })
└── Layout
    └── Navbar
        └── UserMenu
            └── UserAvatar  ← faqat shu yerda kerak

Props bilan yetkazish uchun:

function App() {
  const user = { ism: 'Jasur' };
  return <Layout user={user} />;
}

function Layout({ user }) {
  return <Navbar user={user} />;   // Layout ishlatmaydi, uzatadi
}

function Navbar({ user }) {
  return <UserMenu user={user} />; // Navbar ham ishlatmaydi, uzatadi
}

function UserMenu({ user }) {
  return <UserAvatar user={user} />; // UserMenu ham ishlatmaydi, uzatadi
}

function UserAvatar({ user }) {
  return <img alt={user.ism} />; // faqat shu ishlatadi
}

Layout, Navbar, UserMenu — uchala komponent user ni ishlatmaydi. Faqat keyingisiga uzatadi. Bu — prop drilling (props ni "burg'ulash").

Loyiha o'sganda bu muammo kuchayadi. user tuzilishi o'zgarganda — oraliq barcha komponentlarni o'zgartirish kerak.

Context — bu muammoning yechimi. Ma'lumotni "havoga qo'yish" va istalgan komponent uni to'g'ridan-to'g'ri olishi.

App  (user → Contextga qo'yildi)
└── Layout          ← olishi shart emas
    └── Navbar      ← olishi shart emas
        └── UserMenu ← olishi shart emas
            └── UserAvatar ← to'g'ridan-to'g'ri oladi ✅

Context qanday ishlaydi?

Uch qadam bor. Har birini alohida tushuntiramiz.

1-qadam: Context yaratish

import { createContext } from 'react';

const UserContext = createContext(null);

Bu — bo'sh "idish". Hozircha ichida hech narsa yo'q. null — agar Provider bo'lmasa, default qiymat.

2-qadam: Provider — idishni to'ldirish

function App() {
  const [user, setUser] = useState({ ism: 'Jasur', rol: 'admin' });

  return (
    <UserContext.Provider value={user}>
      <Layout />
    </UserContext.Provider>
  );
}

Provider — "idishni to'ldiruvchi". value ga nima bersangiz — shu Context ichidagi barcha komponentlarga yetadi.

Provider ichiga o'ralgan barcha komponentlar — qanchalik chuqur bo'lmasin — bu qiymatga murojaat qila oladi.

3-qadam: useContext — idishdan olish

import { useContext } from 'react';

function UserAvatar() {
  const user = useContext(UserContext); // to'g'ridan-to'g'ri oldi

  return <img alt={user.ism} />;
}

Props orqali uzatmasdan — to'g'ridan-to'g'ri olindi. Layout, Navbar, UserMenu bu haqda bilmaydi ham.


To'liq misol — tema (dark/light)

Tema — Context uchun eng klassik misol. Har bir komponent temani bilishi kerak, lekin props orqali uzatish — juda ko'p joy.

// TemaContext.jsx
import { createContext, useContext, useState } from 'react';

// 1. Context yaratish
const TemaContext = createContext();

// 2. Provider — bu komponent App ni o'raydi
export function TemaProvider({ children }) {
  const [qorangu, setQorangu] = useState(false);

  function toggle() {
    setQorangu(prev => !prev);
  }

  // value ga state va funksiyani beramiz
  return (
    <TemaContext.Provider value={{ qorangu, toggle }}>
      {children}
    </TemaContext.Provider>
  );
}

// 3. Custom hook — har joyda import qilish o'rniga
export function useTema() {
  return useContext(TemaContext);
}

Nima uchun useTema() degan alohida hook yozamiz? Har joyda useContext(TemaContext) yozish o'rniga — useTema() yozish qisqaroq va tushunarli. Agar Context nomi o'zgarganda — faqat bir joyni o'zgartirish yetarli.

// App.jsx — Provider bilan o'rash
import { TemaProvider } from './TemaContext';

function App() {
  return (
    <TemaProvider>
      <Navbar />
      <Asosiy />
    </TemaProvider>
  );
}
// Navbar.jsx — props kerak emas
import { useTema } from './TemaContext';

function Navbar() {
  const { qorangu, toggle } = useTema();

  return (
    <nav style={{ background: qorangu ? '#1a1a1a' : '#ffffff' }}>
      <span>DevDocs</span>
      <button onClick={toggle}>
        {qorangu ? '☀️ Yorug\'' : '🌙 Qorang\'i'}
      </button>
    </nav>
  );
}
// Asosiy.jsx — ham ishlatishi mumkin, props shart emas
import { useTema } from './TemaContext';

function Asosiy() {
  const { qorangu } = useTema();

  return (
    <main style={{ color: qorangu ? '#ffffff' : '#000000' }}>
      Kontent
    </main>
  );
}

Context qachon qayta chizadi?

Bu muhim — bilmasangiz ilovangiz sekin ishlashi mumkin.

Provider value o'zgardi
        │
        ▼
useContext(ShuContext) ishlatgan BARCHA komponentlar qayta chiziladi

Shuning uchun bir Context ichiga hamma narsani solmang:

// ❌ Yomon — user o'zgarganda HAMMA qayta chiziladi
<AppContext.Provider value={{ user, tema, til, savat }}>

// ✅ Yaxshi — alohida contextlar
<TemaProvider>
  <UserProvider>
    <SavatProvider>
      ...
    </SavatProvider>
  </UserProvider>
</TemaProvider>

Har bir Context faqat o'ziga tegishli narsani saqlaydi. tema o'zgarganda — faqat temani ishlatgan komponentlar qayta chiziladi.


Tez-tez uchraydigan xato

Provider dan tashqarida ishlatish:

// ❌ Provider yo'q — null qaytadi
function App() {
  return <Navbar />; // TemaProvider yo'q!
}

function Navbar() {
  const { qorangu } = useTema(); // { qorangu: undefined }
  return <nav style={{ background: qorangu ? ... }}>; // ishlaydi, lekin noto'g'ri
}

Yechim — custom hook ichida tekshirish:

export function useTema() {
  const context = useContext(TemaContext);
  if (!context) {
    throw new Error('useTema faqat TemaProvider ichida ishlatilishi mumkin');
  }
  return context;
}

Endi Provider unutilsa — aniq xato xabari chiqadi.


Xulosa

createContext()           → bo'sh idish yaratish
<Context.Provider value>  → idishni to'ldirish
useContext(Context)        → idishdan olish

App
└── Provider (value={...})
    └── istalgan chuqurlikdagi komponent
        └── useContext() → props shart emas

Context — prop drilling muammosini hal qiladi. Foydalanuvchi ma'lumoti, tema, til — bular uchun mos. Lekin hamma narsani Context ga solmang — tez-tez o'zgaradigan ma'lumot uchun Zustand yaxshiroq.


Keyingi dars

18-dars: useContext — global state, prop drilling muammosini hal qilish