Context
Elevar el estado funciona bien cuando los componentes que comparten un valor están cerca uno del otro. Se vuelve tedioso cuando no lo están. Un valor que vive cerca de la parte superior del árbol y se necesita seis niveles más abajo tiene que pasarse a través de cada componente en el medio, y cada uno de esos componentes tiene que aceptar y redirigir una prop que nunca usa. Ese relé es prop drilling, el problema en el que cayeron los Componentes compuestos y no pudieron resolverlo con Children.map. Context es la solución integrada de React: un componente provee un valor, y cualquier componente debajo puede leerlo directamente, sin importar cuántas capas haya en medio.
Piénsalo como teletransportar los datos. Los componentes en el medio nunca los ven ni los redirigen; sus listas de props se enfocanen lo que les importa.
Crear un context y proveer un valor
Context tiene tres partes: crearlo, proveer un valor, leer el valor. La creación sucede una vez, en el nivel superior de un archivo, fuera de cualquier componente. Generalmente también lo exportarás, porque los componentes que lo leen suelen vivir en otros archivos.
import { createContext } from 'react'
export const ThemeContext = createContext()
export default function App() {
return (
<ThemeContext value="light">
<Header />
<Button />
</ThemeContext>
)
}Renderizar el objeto context como un componente hace que todo lo que está dentro sea un consumidor en espera. La prop value es la carga: aquí es la cadena "light", pero puede ser cualquier valor de JavaScript, un objeto, un array, incluso una función. Lo que pongas ahí es lo que recibirán los lectores debajo. La prop debe escribirse value; ese nombre es parte de la API.
La ubicación importa. El proveedor no tiene que envolver toda tu aplicación, y normalmente no debería. Colócalo alrededor del subárbol más pequeño que cubra cada componente que necesite el valor. Si solo una esquina de la interfaz se preocupa por el tema, provee el tema en el ancestro común de esa esquina y deja el resto del árbol fuera.
Nota de versión
Antes de React 19, un context no podía renderizarse directamente; renderizabas <ThemeContext.Provider value="light"> en su lugar. React 19 hizo que el objeto context en sí sea renderizable como el proveedor. La forma .Provider aún funciona y es lo que verás en la mayoría de las bases de código existentes y en las lecciones del curso, pero React planea deprecarla en una versión futura e incluye un codemod para el cambio, así que opta por la forma básica <ThemeContext> en código nuevo.
Leer el valor con useContext
Cualquier componente bajo el proveedor lee el valor con useContext, el hook presentado por primera vez en Hooks, pasándole el objeto context para que React sepa cuál context leer. Por eso el context se exporta.
import { useContext } from 'react'
import { ThemeContext } from './App'
function Header() {
const theme = useContext(ThemeContext)
return (
<header className={`${theme}-theme`}>
<h1>{theme === 'light' ? 'Light' : 'Dark'} Theme</h1>
</header>
)
}useContext(ThemeContext) devuelve lo que el ThemeContext más cercano arriba de este componente está sosteniendo actualmente: aquí, la cadena "light". No hay enhebrado de props entre App e Header, y componentes entre ellos en un árbol más grande no mencionarían el tema en absoluto. Una aplicación puede tener varios contexts a la vez, cada uno creado por separado y cada uno leído pasando su propio objeto a useContext.
Hacerlo funcionar: estado más context
Un value="light" codificado nunca cambia. El patrón que hace útil el context lo empareja con state: el estado es propietario del valor y lo actualiza, el context lo entrega. Pasa un objeto que contenga tanto el valor actual como una función que lo cambie, y cada consumidor puede leer o actualizar el tema desde cualquier lugar bajo el proveedor.
export const ThemeContext = createContext()
export default function App() {
const [theme, setTheme] = useState('light')
function toggleTheme() {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light')
}
return (
<ThemeContext value={{ theme, toggleTheme }}>
<Header />
<Button />
</ThemeContext>
)
}
function Button() {
const { theme, toggleTheme } = useContext(ThemeContext)
return (
<button className={`${theme}-theme`} onClick={toggleTheme}>
Switch theme
</button>
)
}Cuando se hace clic en el botón, toggleTheme se ejecuta, el estado en App se actualiza, App se renderiza de nuevo, y el proveedor pasa el nuevo objeto hacia abajo. Cada componente que lee el context se renderiza de nuevo con el valor actualizado. Los consumidores desestructuran solo las propiedades que necesitan.
Este truco de alcance también funciona en pequeña escala. Un componente como un menú desplegable puede renderizar un proveedor alrededor de sus propios hijos, dándole a las piezas dentro una estado compartido que nunca se filtra al resto de la página. Aquí está el menú de Componentes compuestos, terminado:
const MenuContext = createContext()
function Menu({ children }) {
const [open, setOpen] = useState(false)
const toggle = () => setOpen(prevOpen => !prevOpen)
return <MenuContext value={{ open, toggle }}>{children}</MenuContext>
}
function MenuButton({ children }) {
const { toggle } = useContext(MenuContext)
return <button onClick={toggle}>{children}</button>
}Menu es propietario de open y coloca tanto él como toggle en el context; MenuButton sube a buscar el toggle con useContext, y un MenuDropdown escrito de la misma manera lee open para decidir si renderizar. Nada en el medio redirige nada, así que un llamador puede envolver el botón en tantos divs de diseño como quiera y el menú sigue funcionando.
Context permite que un componente cerca de la parte superior diga "aquí está el valor" y permite que cualquier componente debajo suba y lo tome directamente con useContext. Los componentes en el medio nunca lo tocan.
Emparéjalo con estado y el valor puede cambiar también: mantén el valor y su función de actualización juntos en el proveedor, y cualquier consumidor puede leerlo o cambiarlo.
Próximo: Render props y componentes sin cabeza, donde un componente provee comportamiento y te permite suministrar el marcado.

