Mundarija (25)
- Bu darsda
- 1. Nega bu kerak?
- 2. Provider — client fayl, sahifalar — server
- 2.1 Kanondagi TemaProvider
- 2.2 Root layout'da
- 2.3 Natija oynasida: provider va server bolalar
- 3. providers.tsx — hamma provider bir joyda
- 3.1 Naqsh
- 3.2 Tuzoq: QueryClient ni modul darajasida yaratish
- 3.3 Kanonga TanStack Query kirmadi
- 4. 3-tomon kutubxonalar
- 4.1 "use client" siz kutubxona
- 4.2 window ga import paytida tegadigan kutubxonalar
- 5. Provider'ni qayerga qo'yish?
- 5.1 Server komponentlarda Context o'rniga nima?
- 6. Portfolio qadami: tema provider (1-qism)
- 7. Ko'p uchraydigan xatolar
- 8. Mashqlar
- 1-mashq (oson): to'g'ri joy
- 2-mashq (o'rta): provider'larni birlashtirish
- 3-mashq (qiyin): provider tashqarisida — aniq xato
- 4-mashq: Portfolio qadami
- 9. Real ishda
- Xulosa
- Manbalar
Next.js provider'lari: Context, tema va 3-tomon kutubxonalar App Router'da
Qisqacha: React Context faqat client komponentda ishlaydi — Server Component'da
createContextbuild xatosi beradi. Shuning uchun provider alohida"use client"faylga chiqariladi va root layout'da{children}ni o'raydi. Sahifalar baribir server bo'lib qoladi: provider ularni prop sifatida oladi (kompozitsiya naqshi). Bir nechta provider bo'lsa — bittaproviders.tsxga yig'iladi. Brauzerga bog'liq kutubxonalar uchun —"use client"o'rash fayli yoki client komponent ichidanext/dynamicbilanssr: false.
Bu darsda
- Tema provider'ini yozasiz va root layout'ga qo'yasiz — sahifalar serverda qoladi.
providers.tsxnaqshini vaQueryClientProviderni App Router'da xavfsiz ulashni bilasiz.- Server'da
new QueryClient()ni modul darajasida yaratish nega xavfli ekanini tushuntirasiz. windowga bog'liq kutubxonaninext/dynamicvassr: falsebilan ulaysiz (va bu nega faqat client komponentda ishlashini ko'rasiz).portfolioga v1 dagi tema tanlovini qaytarasiz.
Oldin bilishingiz kerak: Server va Client kompozitsiyasi, Context I, useSyncExternalStore, Qorong'i rejim va temalash.
1. Nega bu kerak?
v1 portfolio'da footer'da tema tanlovi bor edi — Qorong'i rejim darsidagi uchta radio: «Tizim», «Yorug'», «Qorong'i». Tanlov localStorage da saqlanardi, <html data-tema> atributi CSS ranglarini almashtirardi. v2 ga uni qaytaramiz.
Tema — butun ilovaning holati. Footer'dagi tanlov uni o'zgartiradi, boshqa joylar (masalan kelajakdagi kod bloklari ranglari) uni o'qiydi. React'da bunday umumiy holat uchun Context bor: provider tepada qiymat beradi, pastdagi istalgan komponent use(Kontekst) bilan oladi.
Muammo: Context — client narsa. Holat brauzerda yashaydi, provider qayta chizilganda iste'molchilar yangilanadi. Server Component'da bularning hech biri yo'q. Biz sinadik — server sahifada createContext:
Error: You're importing a module that depends on `createContext` into a React Server Component module. This API is only available in Client Components. To fix, mark the file (or its parent) with the `"use client"` directive.Server Components darsidagi useState xatosining egizagi. Unda root layout'ni "use client" qilsakmi? Unda hamma sahifa client bo'lib qoladi — marked, sirlar, async sahifalar — hammasi buziladi. Bu darsning javobi — provider'ni kichik client faylga chiqarish.
Hayotiy o'xshatish: binodagi elektr tarmog'i. Elektr (Context qiymati) hamma xonaga yetadi, lekin hisoblagich va avtomat (provider) bitta — kirishdagi kichik shkafda. Xonalar (sahifalar) o'z holicha qoladi, ular faqat rozetkadan foydalanadi.
2. Provider — client fayl, sahifalar — server
2.1 Kanondagi TemaProvider
"use client";
import { createContext, use, useCallback, useSyncExternalStore } from "react";
// Tema provider (N-12): Context faqat Client Component'da.
// Root layout uni import qiladi, lekin children (server
// sahifalar) serverda qoladi — provider ularni prop sifatida oladi.
export type Tema = "tizim" | "yorug" | "qorongi";
const KALIT = "tema";
const HODISA = "tema:ozgardi"; // v1 W-20 dagi hodisa nomi
function oqi(): Tema {
try {
const t = localStorage.getItem(KALIT);
return t === "yorug" || t === "qorongi" ? t : "tizim";
} catch {
return "tizim"; // localStorage o'chirilgan (maxfiy rejim, sandbox)
}
}
function obuna(xabarBer: () => void) {
window.addEventListener(HODISA, xabarBer);
window.addEventListener("storage", xabarBer); // boshqa tabda o'zgarsa
return () => {
window.removeEventListener(HODISA, xabarBer);
window.removeEventListener("storage", xabarBer);
};
}
const TemaKonteksti = createContext<{
tema: Tema;
temaniQoy: (t: Tema) => void;
} | null>(null);
export function TemaProvider({ children }: { children: React.ReactNode }) {
// getServerSnapshot — serverda va hydration paytida "tizim";
// keyin brauzer qiymati. Mismatch xatosi yo'q (N-13).
const tema = useSyncExternalStore(obuna, oqi, () => "tizim" as const);
const temaniQoy = useCallback((t: Tema) => {
try {
if (t === "tizim") localStorage.removeItem(KALIT);
else localStorage.setItem(KALIT, t);
} catch {
// saqlab bo'lmasa ham shu sahifada ishlayveradi
}
if (t === "tizim") delete document.documentElement.dataset.tema;
else document.documentElement.dataset.tema = t;
window.dispatchEvent(new Event(HODISA));
}, []);
return <TemaKonteksti value={{ tema, temaniQoy }}>{children}</TemaKonteksti>;
}
export function useTema() {
const qiymat = use(TemaKonteksti);
if (!qiymat) throw new Error("useTema faqat <TemaProvider> ichida ishlaydi");
return qiymat;
}Kanon izohlari telefonga moslab bo'lindi; bir nechta kod qatori kanonda 70 belgidan uzun. Asosiy qismlar:
| Qism | Nima qiladi |
|---|---|
"use client" |
Provider — client chegarasi fayli |
oqi() |
localStorage dan tema; xato bo'lsa — "tizim" |
obuna() |
O'z hodisamiz (tema:ozgardi) va boshqa tabdagi storage hodisasi |
useSyncExternalStore(obuna, oqi, () => "tizim") |
Tashqi manbaga obuna; uchinchi argument — server qiymati |
temaniQoy |
Saqlaydi, <html data-tema> ni qo'yadi, hodisa chiqaradi |
<TemaKonteksti value> |
React 19: Context'ning o'zi provider (.Provider shart emas) |
useTema() |
use() bilan o'qiydi; provider tashqarisida — aniq xato |
Tema manbasi — Context emas, localStorage. Context faqat uni pastga tarqatadi. useSyncExternalStore darsidagi naqsh: brauzer saqlagan narsani React holatiga ulash. Uning uchinchi argumenti — server snapshot — serverda chizishda ishlatiladigan qiymat. Nega aynan "tizim" va bu nega muhim — keyingi darsda.
window.dispatchEvent(new Event(HODISA)) — Custom events darsidagi o'z hodisamiz. v1 da ham tema shu nom bilan e'lon qilingan edi — kanon nomni saqladi.
2.2 Root layout'da
<body>
<a className="otish" href="#asosiy">
Asosiy kontentga o'tish
</a>
<TemaProvider>
<SaytSarlavhasi />
{children}
<SaytPastki />
</TemaProvider>
</body>Root layout — server. U TemaProvider ni import qiladi (client chegarasidan o'tish) va unga children beradi. SaytSarlavhasi, sahifalar ({children}), SaytPastki — hammasi server komponentlar, lekin ular provider'ning ichida turibdi. Bu — Kompozitsiya darsidagi naqshning o'zi: provider ularni import qilmaydi, faqat qabul qiladi.
Provider ichidagi client komponentlar (TemaTanlov) Context'ni o'qiy oladi. Server komponentlar esa o'qiy olmaydi va o'qishi shart emas: ular brauzerda qayta chizilmaydi.
flowchart TB
L["RootLayout — server"] --> P["TemaProvider — client"]
P --> H["SaytSarlavhasi — server"]
P --> S["sahifalar — server"]
P --> F["SaytPastki — server"]
F --> T["TemaTanlov — client<br/>useTema()"]SaytPastki server komponent, lekin u TemaTanlov ni chizadi — va TemaTanlov provider'ning qiymatini oladi. Context React daraxti bo'ylab tarqaladi, fayl importlari bo'ylab emas. Server komponent oraliqda tursa ham, client bolalari provider'ni ko'radi.
2.3 Natija oynasida: provider va server bolalar
Kanon provider'ining modeli. localStorage o'rniga xotiradagi oddiy "do'kon" — natija oynasida saqlash sahifa yangilanganda tozalanadi, natija har doim bir xil bo'lishi uchun. Page — "server" komponent o'rnida.
import {
createContext,
use,
useSyncExternalStore,
type ReactNode,
} from "react";
type Theme = "tizim" | "yorug" | "qorongi";
// localStorage o'rnida: qiymat + obunachilar
const store = {
value: "tizim" as Theme,
listeners: new Set<() => void>(),
set(t: Theme) {
store.value = t;
store.listeners.forEach((l) => l());
},
subscribe(l: () => void) {
store.listeners.add(l);
return () => store.listeners.delete(l);
},
};
const ThemeContext = createContext<Theme | null>(null);
function ThemeProvider({ children }: { children: ReactNode }) {
const theme = useSyncExternalStore(
store.subscribe,
() => store.value,
() => "tizim" as const,
);
return <ThemeContext value={theme}>{children}</ThemeContext>;
}
function ThemePicker() {
const theme = use(ThemeContext);
return (
<p>
Mavzu: {theme}{" "}
<button onClick={() => store.set("qorongi")}>Qorong'i</button>
</p>
);
}
function Page() {
console.log("Page chizildi");
return <h1>Bosh sahifa</h1>;
}
export default function App() {
return (
<ThemeProvider>
<Page />
<ThemePicker />
</ThemeProvider>
);
}Konsolda:
Page chizildiEkranda:
Bosh sahifa
Mavzu: qorongi Qorong'i«Qorong'i» bosildi — provider yangi qiymat oldi, ThemePicker qayta chizildi. "Page chizildi" esa bir marta: Page elementi App da yaratilgan va provider'ga children bo'lib kelgan — provider qayta chizilganda u o'zgarmaydi (Kompozitsiya darsidagi qoida). Next.js'da Page server komponent — u brauzerda umuman chizilmaydi.
Tekshirib ko'ring:
SaytSarlavhasi(server) ichidauseTema()chaqirsak nima bo'ladi?
Javob
Xato. useTema ichida use(TemaKonteksti) — hook va Context, ular faqat client komponentda. Server komponentga tema kerak bo'lsa (masalan logoning varianti), u kichik client komponentga ajratiladi — barg qoidasi (Client Components). Ko'pincha esa CSS yetadi: [data-tema="qorongi"] .logo { … } — JS'siz.
3. providers.tsx — hamma provider bir joyda
3.1 Naqsh
Real loyihada provider'lar ko'payadi: tema, server ma'lumoti keshi (TanStack Query), tarjimalar, bildirishnomalar. Har birini root layout'ga alohida qo'yish layout'ni chalkashtiradi. Odatiy yechim — bitta client fayl:
// src/app/providers.tsx
"use client";
import {
QueryClient,
QueryClientProvider,
} from "@tanstack/react-query";
import { useState } from "react";
import { TemaProvider } from "@/komponentlar/TemaProvider";
type Props = { children: React.ReactNode };
export function Providers({ children }: Props) {
const [queryClient] = useState(() => new QueryClient());
return (
<QueryClientProvider client={queryClient}>
<TemaProvider>{children}</TemaProvider>
</QueryClientProvider>
);
}Root layout'da esa bitta qator: <Providers>{children}</Providers>.
3.2 Tuzoq: QueryClient ni modul darajasida yaratish
useState(() => new QueryClient()) — bejiz emas. TanStack Query darsida SPA'da const queryClient = new QueryClient() ni fayl boshida yozgan edingiz — u yerda bu to'g'ri edi: brauzerda bitta foydalanuvchi.
Next.js'da client komponent serverda ham chiziladi (Client Components darsidagi topilma). Server esa — bitta jarayon, hamma foydalanuvchilar uchun. Modul darajasidagi new QueryClient() server xotirasida bir marta yaratiladi va hamma so'rovlar uchun umumiy bo'lib qoladi. Ali'ning so'rovida keshlangan ma'lumot Vali'ning sahifasiga chiqib qolishi mumkin. useState ichida yaratilsa — har so'rov (va har brauzer) o'z nusxasini oladi.
Qoida kengroq: server tomonda modul darajasidagi o'zgaruvchan holat — hamma foydalanuvchilarniki. Provider uchun holat — faqat komponent ichida.
3.3 Kanonga TanStack Query kirmadi
Kanon portfolio da providers.tsx va TanStack Query yo'q. Sabab oddiy: client'da server ma'lumoti olinmaydi. Loyihalar, blog, GitHub profili — hammasi Server Component'larda, to'g'ridan. 19-qism o'lchovi bo'yicha Query birinchi yuklashga ~10 KB gzip qo'shadi — foydasiz qo'shilardi. Kanonda bitta provider (tema), shuning uchun u root layout'da to'g'ridan turibdi. Query qachon kerakligi — TanStack Query bilan Next.js darsida.
4. 3-tomon kutubxonalar
4.1 "use client" siz kutubxona
Ko'p kutubxonalar Next.js'gacha yozilgan: ichida hook'lar bor, lekin fayllarida "use client" yo'q. Server sahifaga to'g'ridan qo'yilsa — depends on useState turidagi xato. Yechim Kompozitsiya darsida ko'rgan o'rash fayli:
// komponentlar/Karusel.tsx
"use client";
export { Carousel as Karusel } from "boshqa-kutubxona";Provider'lar ham shunday: kutubxonaning ThemeProvider, IntlProvider kabi komponentlari o'z "use client" faylingiz orqali ulanadi — providers.tsx aynan shu vazifani bajaradi.
4.2 window ga import paytida tegadigan kutubxonalar
Ba'zi kutubxonalar (xarita, grafik, matn muharrirlari) import paytidayoq window yoki document ga murojaat qiladi. "use client" ham yordam bermaydi — client komponent serverda ham chiziladi, import serverda ham bajariladi, natija — window is not defined.
Yechim — next/dynamic bilan kechiktirilgan import va ssr: false: "bu komponentni serverda umuman chizma, faqat brauzerda yukla".
// komponentlar/XaritaOrol.tsx
"use client";
import dynamic from "next/dynamic";
const Xarita = dynamic(() => import("./Xarita"), {
ssr: false,
loading: () => <p>Xarita yuklanmoqda…</p>,
});
export function XaritaOrol() {
return <Xarita />;
}ssr: false ni server komponentda yozsak-chi? Biz sinadik:
Error: `ssr: false` is not allowed with `next/dynamic` in Server Components. Please move it into a Client Component.Tarjimasi: "Server Component'larda next/dynamic bilan ssr: false mumkin emas. Uni Client Component'ga ko'chiring". Shuning uchun yuqoridagi misolda "use client" li XaritaOrol o'rami bor — server sahifa <XaritaOrol /> ni bemalol ishlatadi.
Narxi: ssr: false komponenti HTML'da yo'q — birinchi ochilishda loading matni ko'rinadi, keyin JS yuklanib, chiziladi. Qidiruv boti xaritani ko'rmaydi. Faqat haqiqatan brauzersiz ishlamaydigan narsalar uchun ishlating. next/dynamic — React'ning lazy iga Next.js qo'shgan qulay o'ram.
Tekshirib ko'ring: grafik kutubxonasi faqat
useEffectichidawindowga tegadi.ssr: falsekerakmi?
Javob
Yo'q. Effekt serverda bajarilmaydi — oddiy "use client" yetarli, komponent serverda bo'sh holatda chiziladi. ssr: false faqat import paytida yoki chizish paytida window ga tegadigan kutubxonalar uchun.
5. Provider'ni qayerga qo'yish?
| Joy | Qachon |
|---|---|
| Root layout | Butun saytga kerak: tema, tarjimalar |
| Bo'lim layout'i | Faqat bir qismga: admin paneli holati, savatcha |
| Komponent ichida | Bitta vidjet: forma holati, karusel |
Qoida: provider'ni iloji boricha pastga. Root layout'dagi provider ham sahifalarni client qilmaydi (kompozitsiya), lekin har provider o'zi JS va brauzerda ishlaydigan kod. Faqat admin panelda kerak bo'lgan provider butun ochiq saytga yuklanmasin.
Hujjat yana bir sabab keltiradi: provider qanchalik chuqur bo'lsa, Next.js sahifaning statik qismlarini shunchalik yaxshi optimallaydi.
5.1 Server komponentlarda Context o'rniga nima?
Savol tug'iladi: server komponentlar bir-biriga umumiy ma'lumotni qanday uzatadi? Masalan, joriy foydalanuvchi — layout'ga ham, sahifaga ham, chuqurdagi kartaga ham kerak. Props bilan pastga uzatish ("props drilling") — Context I darsidagi muammo.
Server tomonda javob boshqacha: har komponent kerakli ma'lumotni o'zi so'raydi — await joriyFoydalanuvchi(). Uch joyda chaqirilsa, uch marta bazaga borish kerakmi? Yo'q: React'ning cache() funksiyasi bitta so'rov davomida natijani eslab qoladi — birinchi chaqiruv bazaga boradi, qolganlari xotiradan oladi. Bu naqsh — React cache() va Data Access Layer darsida. Hozircha qoida: client daraxtda — Context, server daraxtda — keshlangan funksiya chaqiruvi.
6. Portfolio qadami: tema provider (1-qism)
Bu dars va keyingi dars holat 13 ni quradi. Commit hali qilmang.
| Fayl | Nima |
|---|---|
komponentlar/TemaProvider.tsx |
Client: Context + useSyncExternalStore + useTema() |
komponentlar/TemaTanlov.tsx |
Client: v1 dagi radio fieldset (C-87) |
komponentlar/SaytPastki.tsx |
Footer'ga <TemaTanlov /> |
app/layout.tsx |
<TemaProvider> header, sahifalar va footer'ni o'raydi |
app/globals.css |
:root[data-tema="qorongi"], [data-tema="yorug"], tizim afzalligi :root:not([data-tema]) |
CSS'dagi tartib v1 dagidek: atribut yo'q bo'lsa — prefers-color-scheme (tizim), bor bo'lsa — tanlangan mavzu. Birinchi bo'yashdan oldingi skript (TemaSkripti) va suppressHydrationWarning — keyingi darsda: ularsiz qorong'i tanlagan odam bir lahza oq sahifa ko'radi.
Kanon o'lchovi: JS 143.4 → 143.9 KB (+0.5 KB — provider va tanlov). TanStack Query qo'shilmadi.
7. Ko'p uchraydigan xatolar
| Belgi | Sabab | Davosi |
|---|---|---|
… depends on createContext into a React Server Component |
Context server faylda | Provider'ni "use client" faylga |
| Hamma sahifa client bo'lib qoldi | Root layout "use client" |
Faqat provider client, layout server |
useTema faqat <TemaProvider> ichida ishlaydi |
Komponent provider tashqarisida | Provider'ni yuqoriroqqa |
| Foydalanuvchilar bir-birining ma'lumotini ko'radi | Modul darajasidagi new QueryClient() |
useState(() => new QueryClient()) |
`ssr: false` is not allowed … in Server Components |
next/dynamic server faylda |
"use client" o'ramiga ko'chiring |
window is not defined kutubxona importida |
Kutubxona import paytida brauzerga tegadi | next/dynamic + ssr: false |
| Kutubxona komponenti hook xatosi beradi | Kutubxonada "use client" yo'q |
O'z o'rash faylingiz |
8. Mashqlar
Mashqlar kurs/mashqlar/23/12-providerlar/ da.
1-mashq (oson): to'g'ri joy
- Butun saytdagi til (uz/en) provider'i —
- Faqat
/admindagi yon panel holati — - Bitta formaning ichki holati —
new QueryClient()qayerda yaratiladi —
Yechim
- root layout — hamma sahifaga kerak.
- admin layout —
app/admin/layout.tsx; ochiq sayt uni yuklamaydi. - komponent ichida — Context shart ham emas,
useStateyetadi. - useState —
useState(() => new QueryClient()): har so'rov va brauzerga o'z nusxasi.
2-mashq (o'rta): provider'larni birlashtirish
Funksiya yozing: composeProviders(providers) — provider komponentlar massivini oladi va bitta Providers komponentini qaytaradi: birinchi element — eng tashqi. LangProvider ("uz") va ThemeProvider ("yorug") bilan sinang; ichkaridagi Status ikkala qiymatni ko'rsatsin. Ishora: reduceRight bilan ichkaridan tashqariga o'rang.
Yechim
import { createContext, use, type ReactNode } from "react";
type Wrapper = (p: { children: ReactNode }) => ReactNode;
function composeProviders(providers: Wrapper[]): Wrapper {
return function Providers({ children }) {
return providers.reduceRight<ReactNode>(
(inner, Provider) => <Provider>{inner}</Provider>,
children,
);
};
}
const LangContext = createContext("en");
const ThemeContext = createContext("tizim");
const LangProvider: Wrapper = ({ children }) => (
<LangContext value="uz">{children}</LangContext>
);
const ThemeProvider: Wrapper = ({ children }) => (
<ThemeContext value="yorug">{children}</ThemeContext>
);
const Providers = composeProviders([LangProvider, ThemeProvider]);
function Status() {
return (
<p>
til: {use(LangContext)}, mavzu: {use(ThemeContext)}
</p>
);
}
export default function App() {
return (
<Providers>
<Status />
</Providers>
);
}Ekranda:
til: uz, mavzu: yorugreduceRight oxirgi provider'dan boshlaydi: avval ThemeProvider children ni o'raydi, keyin LangProvider uni. Natija — <LangProvider><ThemeProvider>…. Provider'lar o'nta bo'lsa ham, root layout'da bitta <Providers> qoladi. Tartib muhim bo'lishi mumkin: bir provider ikkinchisining qiymatini ishlatsa, u ichkarida turishi kerak.
3-mashq (qiyin): provider tashqarisida — aniq xato
Til provider'i yozing: LangProvider va useLang(). useLang provider tashqarisida chaqirilsa — "useLang faqat <LangProvider> ichida ishlaydi" xatosini tashlasin (Context sukut qiymati — null). App da ikki joyda sinang: provider ichida (Til: uz) va tashqarida — xatoni error boundary ushlab, Xato: … deb ko'rsatsin.
Yechim
import { Component, createContext, use, type ReactNode } from "react";
const LangContext = createContext<string | null>(null);
function LangProvider({ children }: { children: ReactNode }) {
return <LangContext value="uz">{children}</LangContext>;
}
function useLang(): string {
const lang = use(LangContext);
if (lang === null) {
throw new Error("useLang faqat <LangProvider> ichida ishlaydi");
}
return lang;
}
class Boundary extends Component<
{ children: ReactNode },
{ message: string | null }
> {
state = { message: null as string | null };
static getDerivedStateFromError(error: Error) {
return { message: error.message };
}
render() {
if (this.state.message) return <p>Xato: {this.state.message}</p>;
return this.props.children;
}
}
function LangLabel() {
return <p>Til: {useLang()}</p>;
}
export default function App() {
return (
<>
<LangProvider>
<LangLabel />
</LangProvider>
<Boundary>
<LangLabel />
</Boundary>
</>
);
}Konsolda:
Error: useLang faqat <LangProvider> ichida ishlaydi
The above error occurred in the <LangLabel> component.
React will try to recreate this component tree from scratch using the error boundary you provided, Boundary.Ekranda:
Til: uz
Xato: useLang faqat <LangProvider> ichida ishlaydiSukut qiymati null va aniq xato — kanondagi useTema ning naqshi. Sukut qiymati "uz" bo'lganida, provider'ni unutish jim o'tardi: komponent ishlayveradi, lekin tilni o'zgartirish unga yetmaydi — topish qiyin xato. Konsoldagi uch qator — React'ning ushlangan xato haqidagi dev yozuvi.
4-mashq: Portfolio qadami
O'z portfolio ingizga TemaProvider, TemaTanlov (footer'da) va CSS'dagi data-tema qoidalarini qo'shing. Tekshiring: next dev da «Qorong'i» ni tanlang — sahifa qorong'i bo'lsin, boshqa tabda ham (storage hodisasi). Keyin sahifani yangilang va birinchi lahzaga e'tibor bering — oq chaqnash ko'rinadimi? Javobini keyingi darsda tuzatamiz.
Yechim
Namuna — «Portfolio qadami» bo'limidagi jadval va kanon TemaProvider. Yangilaganda oq chaqnash ko'rinishi kutilgan: HTML serverda data-tema siz keladi, provider esa faqat JS yuklangandan keyin atributni qo'yadi. Root layout "use client" emasligini tekshiring — TemaProvider faqat o'rovchi. Commit hali qilmang.
9. Real ishda
- Har App Router loyihada
providers.tsxyoki shunga o'xshash fayl bor: Query, tema, tarjima, analitika, bildirishnomalar (toast). next-themes— tema uchun tayyor kutubxona. Kanon uni olmadi: v1 dagi o'z tizimimiz (Qorong'i rejim) bor edi va u kichikroq. Kutubxona qachon to'g'ri tanlov — Stil va tema darsida.- Server'dagi umumiy holat — haqiqiy hodisalar bor: modul darajasidagi kesh tufayli bir foydalanuvchining ma'lumoti boshqasiga ko'ringan. Kod tekshiruvida "bu o'zgaruvchi serverda hamma uchun umumiy emasmi?" — muhim savol.
next/dynamic+ssr: false— xaritalar (Leaflet), matn muharrirlari, eski grafik kutubxonalari uchun kundalik vosita.
Xulosa
- Context va provider — faqat client: provider
"use client"faylda, root layout esa server bo'lib qoladi. - Provider
childrenni qabul qiladi — sahifalar va header/footer server komponent bo'lib qoladi; Context React daraxti bo'ylab tarqaladi. - Ko'p provider — bitta
providers.tsx;QueryClient—useStateichida, modul darajasida emas. - Kutubxona
"use client"siz — o'rash fayli; import paytidawindow—next/dynamic+ssr: false(faqat client komponentda). - Kanon: bitta
TemaProvider(+0.5 KB), TanStack Query kerak bo'lmadi.
Keyingi dars: Hydration va hydration xatolari — oq chaqnashni yo'qotamiz va server bilan brauzer "kelishmasa" nima bo'lishini haqiqiy tajribalar bilan ko'ramiz.
Manbalar
- Next.js hujjati — Server and Client Components: Context providers, Third-party components: https://nextjs.org/docs/app/getting-started/server-and-client-components (next 16.4.0 paketi ichidagi hujjat, 2026-10-21 da o'qildi)
- Next.js hujjati — Lazy loading (
next/dynamic): https://nextjs.org/docs/app/guides/lazy-loading - TanStack Query hujjati — Advanced Server Rendering (QueryClient har so'rovga): https://tanstack.com/query/latest/docs/framework/react/guides/advanced-ssr
- Kanon o'lchovlari:
portfoliov2, holat 13 (JS +0.5 KB, Query kirmadi).createContextvassr: falseserver komponentda — biz ishga tushirdik (next 16.4.0,next build).
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!