useContext — Context nima, Provider, Consumer
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