Learning
Všechny články
Frontend31. srpna 2026 5 min

Context je vyhledávání nahoru, ne kanál dolů

Hodnotu si komponenta při renderu najde, nikdo jí ji neposílá

Hodnotu si komponenta najde sama, nikdo jí ji neposílá

Context se skoro vždycky vysvětluje jako potrubí. Nahoře nalijeme hodnotu, dole vyteče. Ten obraz je pohodlný a vede k špatným odhadům o výkonu.

Přesnější popis je opačný. Když komponenta při renderu zavolá useContext(SomeContext), React jde od ní nahoru po stromě a hledá nejbližší provider téhle konkrétní identity contextu. Najde ho a vezme jeho hodnotu. Nenajde, vezme výchozí hodnotu z createContext(). Nic neteče dolů, čte se nahoru.


Proč je to stejný mechanismus jako v CSS

Dokumentace to přirovnává k dědičnosti CSS a to přirovnání sedí líp, než se zdá. Vnořený element dědí color z nejbližšího předka, který ho nastavil, dokud ho někdo blíž nepřepíše. Provider vnořený hlouběji přepisuje ten nad sebou přesně takhle.

Z toho plyne první praktický důsledek. Mezilehlé komponenty jsou v tom vztahu neviditelné. Můžeš mezi provider a konzumenta vložit libovolně hluboký strom a nic se nezmění, protože ty komponenty nejsou po cestě, kterou hodnota jde. Jsou jen po cestě, po které se hledá.

Klíčem toho hledání je objekt vrácený z createContext(), ne jméno proměnné. Dva contexty vytvořené dvěma voláními createContext() se stejným výchozím obsahem spolu nemají nic společného. Proto se createContext volá jednou v modulu a importuje se, ne vytváří v komponentě.


Odkud se bere překreslení

Když se změní hodnota providera, překreslí se všechny komponenty, které ten context čtou, bez ohledu na hloubku. Tady se skrývá to, co se v praxi nejčastěji podcení.

Konzument se překresluje podle identity celé hodnoty, ne podle toho, kterou její část si vzal. Tohle je nejčastější příčina problému:

<AppContext value={{ user, theme, setTheme }}>

Ten literál se vyrábí při každém renderu providera znovu. Nová reference se nerovná staré, takže se překreslí každý konzument. Komponenta, která si z hodnoty bere jen theme, se překreslí i tehdy, když se změnil jen user.

Druhá půlka je ještě méně vidět. Předání přes context neprochází přes memo. memo porovnává props, ale hodnota z contextu není prop, je to čtení uvnitř komponenty. Memoizovaný mezičlánek proto překreslení konzumenta nezastaví.


Kdy context nesahat

Dokumentace je v tomhle nezvykle přímá a stojí za to ji brát vážně. Než sáhneš po contextu, zkus dvě věci.

Nejdřív prosté props. Dvanáct props přes dvanáct komponent je otrava, ale je z nich vidět, kdo co používá. Context tuhle stopu maže.

Pak předání JSX jako children. Když protahuješ posts skrz Layout, který je vůbec nepoužívá, chybějící abstrakce není context, ale komponenta. <Layout><Posts posts={posts} /></Layout> ten průchod ruší úplně.

Context je na místě u věcí, které jsou opravdu okolní: motiv, přihlášený účet, aktuální routa. Tedy tam, kde by jinak stejnou hodnotu potřebovaly vzdálené větve stromu.


Co si z toho odnést

Ber context jako rozsah platnosti, ne jako přenos. Provider vymezuje část stromu, ve které daná hodnota platí, a komponenta si ji při čtení najde. Ta představa předpovídá chování líp: proto funguje vnořené přepsání, proto je jedno, co je mezi tím, a proto ho nezastaví memo.

Když ladíš překreslení, dívej se první na identitu hodnoty. Objektový literál v providerovi zruší veškerou memoizaci pod ním. Buď ho memoizuj, nebo rozděl na víc contextů podle toho, co se mění spolu.

A na prop drilling se nejdřív podívej jako na chybějící kompozici. Context, který jen zkracuje předávání, si vezme cenu v podobě neviditelných závislostí a nedá za ni nic.

Zdroje

Poslech místo čtení — 2 zdroje k načtení.