React Context: Teleport pro data
V klasické komponentové architektuře proudí data jedním směrem – z rodiče do dítěte přes props. Jak ale aplikace roste, začnete řešit prop drilling: předávání údajů přes mnoho vrstev komponent, které tyto údaje samy vůbec nepotřebují.
- •Co je prop drilling a proč je problém
- •K čemu Context API slouží a k čemu ne
- •Tři kroky práce s contextem: vytvoření, provider, consumer
- •Jak zkombinovat useContext a useState pro reaktivní data
- •Jak fungují výchozí hodnoty contextu
- •Kdy Context používat a kdy zůstat u props
Problém zvaný prop drilling
Představte si, že máte URL uživatelova avataru na samém vrchu aplikace (App.js), ale komponenta, která obrázek skutečně renderuje (Avatar.js), je zanořená o deset úrovní níže v layoutech, headerech a menu.
Abyste se k ní dostali s obyčejnými props, musí každý mezilehlý komponent prop přijmout a předat dál – i když ho sám nikdy nepoužije. Přesně tomu se říká prop drilling.
React Context je vestavěné řešení této zbytečné práce. Můžete si ho představit jako teleporter: umožní vám poslat data z vrcholu stromu přímo do libovolně hluboko zanořeného komponentu a všechny mezikroky přeskočit.
Tři kroky práce s Contextem
Abyste tento „teleport“ používali efektivně, stačí zvládnout tři kroky:
- •Vytvoření contextu
- •Poskytování (broadcast) dat
- •Spotřebování (přijímání) dat
Pojďme si to projít na praktickém příkladu: uživatelská autentizace. Chceme mít profil aktuálního uživatele dostupný všude, aniž bychom ho museli ručně předávat každým layoutem.
Krok 1: Vytvoření contextu
Nejprve založíme komunikační kanál pomocí React.createContext. Typicky ho definujete v samostatném souboru a exportujete, aby ho ostatní komponenty mohly importovat a „poslouchat“.
import * as React from "react"
// Create the context.
// We export it so other components can import it to "listen" in.
const UserContext = React.createContext(null)
export default UserContextKrok 2: Poskytování dat (broadcast)
Každý context objekt má komponentu .Provider. Cokoli předáte do jejího prop value, bude dostupné všem komponentům uvnitř Provideru, ať jsou zanořené jakkoli hluboko.
import * as React from "react"
import UserContext from "./UserContext"
import DashboardLayout from "./DashboardLayout"
export default function App() {
// This is the data we want to teleport
const activeUser = {
username: "Neo_TheOne",
role: "Admin",
status: "Active",
lastLogin: "2 minutes ago",
}
return (
// We wrap our component tree in the Provider.
// We pass 'activeUser' into the 'value' prop.
<UserContext.Provider value={activeUser}>
<DashboardLayout />
</UserContext.Provider>
)
}Všimněte si, že mezilehlé komponenty jako layouty, headery nebo menu se objektu activeUser nemusí vůbec dotknout, pokud ho samy nepotřebují.
Krok 3: Spotřebování contextu
Hluboko ve stromu pak komponenta, která data skutečně potřebuje, použije React.useContext a na context se přihlásí.
import * as React from "react"
import UserContext from "./UserContext"
export default function UserProfile() {
// Accessing the teleported data
const user = React.useContext(UserContext)
if (!user) return null
return (
<div className="profile-card">
<h3>Welcome back, {user.username}</h3>
<p>Role: {user.role}</p>
<span className="badge">{user.status}</span>
</div>
)
}Context vs. state: trubky vs. voda
Časté nedorozumění je, že Context je správce stavu. Není. Context je transportní mechanismus.
Pokud je Context soustava trubek, state je voda, která jimi protéká. Contextu je jedno, jestli jsou data statická (konfigurace) nebo dynamická (část stavu).
Aby byla aplikace reaktivní, kombinujete Context (trubku) s useState (zdroj vody).
Kombinace contextu s useState
Upravme náš příklad tak, aby komponenty mohly aktuálního uživatele nejen číst, ale i měnit.
import * as React from "react"
import UserContext from "./UserContext"
import DashboardLayout from "./DashboardLayout"
export default function App() {
// 1. We manage the state here using useState
const [user, setUser] = React.useState({
username: "Neo_TheOne",
status: "Online",
})
// 2. We teleport the state variable AND the setter function
return (
<UserContext.Provider value={{ user, setUser }}>
<DashboardLayout />
</UserContext.Provider>
)
}Nyní může každý komponent, který zavolá useContext(UserContext), user jak číst, tak měnit pomocí setUser. Když se state v App změní, React strom znovu vyrenderuje a novou hodnotu pošle všem consumerům.
Výchozí hodnoty contextu
Při vytváření contextu můžete volitelně zadat výchozí hodnotu. Ta se použije pouze tehdy, když komponent context spotřebuje, ale v jeho nadřazeném stromu nenajde odpovídající Provider.
import * as React from "react"
// Default is "light"
const ThemeContext = React.createContext("light")
function ThemedButton() {
const theme = React.useContext(ThemeContext)
return <button className={`btn-${theme}`}>I am a {theme} button</button>
}
export default function Page() {
return (
<main>
{/* 1. No Provider here: falls back to default "light" */}
<ThemedButton />
{/* 2. Wrapped in a Provider: uses value "dark" */}
<ThemeContext.Provider value="dark">
<ThemedButton />
</ThemeContext.Provider>
</main>
)
}Tento vzor je obzvlášť užitečný pro témata, lokalizaci nebo znovupoužitelné komponenty, které mají fungovat „z krabice“, ale dají se přizpůsobit obalením do Provideru.
Kdy Context použít – a kdy ne
Protože je Context mocný, svádí to použít ho úplně na všechno. Prop drilling ale sám o sobě není špatný – dělá tok dat explicitní a snadno sledovatelný.
Dobré použití Contextu
- •Data, která jsou globální pro větší část stromu (uživatel, téma, jazyk/locale)
- •Hodnoty potřebné na mnoha různých úrovních zanoření
Kdy se Contextu vyhnout
- •Potřebujete data předat jen o jednu až dvě úrovně níž – props jsou jednodušší a přehlednější.
- •Řešíte výkon: aktualizace contextu vysoko ve stromu může způsobit re‑render velkého množství consumerů.
Shrnutí
- •Context je teleport pro data, ne správce stavu.
- •Používáte tři kroky: vytvořit context, obalit část stromu Providerem a číst hodnotu pomocí useContext.
- •Kombinací Contextu a useState sdílíte reaktivní data napříč aplikací.
- •Context použijte, když je cesta přes props zbytečně dlouhá – ale na krátké vzdálenosti se klidně spolehněte na jednoduché props.