Context API en React
El Context API de React es una herramienta poderosa que permite compartir datos entre componentes sin necesidad de pasar props manualmente a través de cada nivel del árbol de componentes. Esto es especialmente útil para datos que son considerados "globales" para una aplicación, como el tema, el usuario autenticado, o la configuración del idioma.
Children
En React, children es una prop especial que representa los nodos hijos que se colocan dentro de un componente cuando se renderiza.
En simples palabras, children
es una propiedad especial en React que permite pasar componentes hijos a un componente padre. Esto es útil para crear componentes más flexibles y reutilizables.
Caso | ¿Por qué usar children ? |
---|---|
✅ Para envolver contenido | children es el patrón ideal para componentes que actúan como contenedores genéricos (layouts, modales, tarjetas, etc.) |
✅ Para evitar props innecesarias | En lugar de definir props como content , body , mainContent , etc., usas solo children . |
✅ Convención de React | Es una práctica común y conocida por todos los desarrolladores React. |
const Card = () => {
return (
<div className="card">
<div className="card-body">
<h2 className="card-title">Card Title</h2>
<p className="card-text">This is a simple card component.</p>
<button className="btn btn-primary">Click Me</button>
</div>
</div>
);
};
export default Card;
import Card from "./components/card";
const App = () => {
return (
<div className="container mx-auto my-5">
<Card />
</div>
);
};
export default App;
Ahora si queremos que el componente Card
reciba contenido dinámico, podemos usar children
:
import Card from "./components/card";
const App = () => {
return (
<div className="container mx-auto my-5">
<Card>
<h2 className="card-title">Card Title</h2>
<p className="card-text">This is a simple card component.</p>
<button className="btn btn-primary">Click Me</button>
</Card>
<Card>
<h2 className="card-title">Card Title</h2>
<p className="card-text">This is a simple card component.</p>
</Card>
<Card>
<p className="card-text">This is a simple card component.</p>
</Card>
</div>
);
};
export default App;
interface Props {
children: React.ReactNode;
}
const Card = ({ children }: Props) => {
return (
<div className="card mb-3">
<div className="card-body">{children}</div>
</div>
);
};
export default Card;
🧠 ¿Qué es un Context en React?
Es una forma de compartir datos entre componentes sin tener que pasarlos manualmente por props.
🔄 Piensa en el Context como una caja compartida de información global (como el idioma, el usuario, el tema oscuro, etc.).
🤝 ¿Qué es un Provider?
El Provider es el componente que envuelve tu aplicación (o una parte de ella) y le da acceso al Context a todos los componentes hijos.
📦 Es como decir: "Todo lo que está dentro de esta caja puede usar la información compartida del Context".
📌 Ejemplo simple con metáfora:
Imagina que creas un IdiomaContext
.
- Context = el idioma actual (
es
,en
,fr
) - Provider = el encargado de ofrecer ese idioma a todo el árbol de componentes
🗺️ Visual:
<IdiomaProvider>
<App /> ← Todos los componentes dentro pueden acceder al idioma global
</IdiomaProvider>
Y dentro de cualquier componente puedes acceder a ese valor con:
const { idioma } = useContext(IdiomaContext);
Auth Provider
El Auth Provider es un patrón común en aplicaciones React para manejar la autenticación de usuarios. Proporciona un contexto que contiene información sobre el usuario autenticado y funciones para iniciar y cerrar sesión.
- createContext: Crea un contexto para compartir datos de autenticación.
- useContext: Permite a los componentes acceder al contexto de autenticación.
- Provider: Envuelve la aplicación para que todos los componentes hijos puedan acceder a los datos de autenticación.
src\context\auth\auth-provider.tsx
import type { ReactNode } from "react";
import { createContext, useContext, useState } from "react";
export interface User {
name: string;
email: string;
}
interface AuthContextType {
user: User | null;
login: () => void;
logout: () => void;
}
export const AuthContext = createContext<AuthContextType | undefined>(
undefined
);
interface Props {
children: ReactNode;
}
const AuthProvider = ({ children }: Props) => {
const [user, setUser] = useState<User | null>(null);
const login = () => {
setUser({
name: "Test User",
email: "test@test.com",
});
};
const logout = () => {
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
};
export default AuthProvider;
export const useAuth = () => {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error("useAuth debe usarse dentro de <AuthProvider>");
}
return context;
};
Preguntas comunes
- ¿Por qué createContext se inicializa con
undefined
?- RE: Para indicar que el contexto aún no tiene un valor definido. Esto ayuda a detectar errores si se intenta usar el contexto fuera de un Provider.
- ¿Por qué usamos
useContext
?- RE: Para acceder al valor del contexto de manera más sencilla y evitar pasar props manualmente a través de múltiples niveles de componentes.
- ¿Qué pasa si no envuelvo mi aplicación con el Provider?
- RE: Si no envuelves tu aplicación con el Provider, los componentes que intenten acceder al contexto recibirán
undefined
, lo que puede causar errores si no se maneja adecuadamente.
- RE: Si no envuelves tu aplicación con el Provider, los componentes que intenten acceder al contexto recibirán
- ¿Puedo tener múltiples contextos en mi aplicación?
- RE: Sí, puedes tener múltiples contextos para diferentes propósitos (por ejemplo, autenticación, tema, idioma, etc.). Cada contexto puede ser creado con
createContext
y envuelto en su propio Provider.
- RE: Sí, puedes tener múltiples contextos para diferentes propósitos (por ejemplo, autenticación, tema, idioma, etc.). Cada contexto puede ser creado con
- ¿Qué es el error: Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components.eslint(react-refresh/only-export-components)?
- RE: Este error ocurre cuando intentas exportar algo que no es un componente React desde un archivo que se espera que solo contenga componentes. Asegúrate de que el archivo solo exporte componentes React y no funciones o constantes que no sean componentes.
Todo Modularizado
src\context\auth\auth-context.ts
import { createContext } from "react";
import type { User } from "../../interfaces/user.interface";
interface AuthContextType {
user: User | null;
login: () => void;
logout: () => void;
}
export const AuthContext = createContext<AuthContextType | undefined>(
undefined
);
src\hooks\use-auth.ts
import { useContext } from "react";
import { AuthContext } from "../context/auth/auth-context";
export const useAuth = () => {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error("useAuth debe usarse dentro de <AuthProvider>");
}
return context;
};
src\interfaces\user.interface.ts
export interface User {
name: string;
email: string;
}
src\context\auth\auth-provider.tsx
import type { ReactNode } from "react";
import { useState } from "react";
import type { User } from "../../interfaces/user.interface";
import { AuthContext } from "./auth-context";
interface Props {
children: ReactNode;
}
const AuthProvider = ({ children }: Props) => {
const [user, setUser] = useState<User | null>(null);
const login = () => {
setUser({
name: "Usuario de prueba",
email: "test@test.com",
});
};
const logout = () => {
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout }}>
{children}
</AuthContext.Provider>
);
};
export default AuthProvider;
Utilizando el AuthProvider
src\main.tsx
import "bootstrap/dist/css/bootstrap.min.css";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import AuthProvider from "./context/auth/auth-provider.tsx";
import "./index.css";
createRoot(document.getElementById("root")!).render(
<StrictMode>
<AuthProvider>
<App />
</AuthProvider>
</StrictMode>
);
src\App.tsx
import LoginButton from "./components/login-button";
import LogoutButton from "./components/logout-button";
import { useAuth } from "./hooks/use-auth";
const App = () => {
const { user } = useAuth();
if (!user) {
return (
<div className="container">
<h1>Please log in</h1>
<LoginButton />
</div>
);
}
return (
<div className="container">
<h1>Welcome, {user.name}!</h1>
<LogoutButton />
</div>
);
};
export default App;
src\components\login-button.tsx
import { useAuth } from "../hooks/use-auth";
const LoginButton = () => {
const { login } = useAuth();
return (
<button
className="btn btn-outline-dark"
onClick={login}
>
Login
</button>
);
};
export default LoginButton;
src\components\logout-button.tsx
import { useAuth } from "../hooks/use-auth";
const LogoutButton = () => {
const { logout } = useAuth();
return (
<button
className="btn btn-outline-danger"
onClick={logout}
>
Logout
</button>
);
};
export default LogoutButton;
Custom Hooks vs Context
Los Custom Hooks y el Context son dos herramientas poderosas en React, pero tienen propósitos diferentes.
Concepto | 🔹 Context | 🔸 Custom Hook |
---|---|---|
¿Qué es? | Mecanismo de React para compartir datos globales | Función reutilizable que encapsula lógica con hooks |
¿Comparte estado entre componentes? | ✅ Sí, todos acceden al mismo value del Provider | ❌ No, cada uso crea un estado independiente |
¿Requiere Provider? | ✅ Sí, es obligatorio usar <Context.Provider> | ❌ No, puedes usarlo donde quieras |
¿Sirve para lógica reutilizable? | ⚠️ No directamente (mejor combinar con un hook) | ✅ Sí, ideal para evitar duplicación de lógica |
¿Sirve para estado global? | ✅ Sí, se accede desde cualquier lugar del árbol | ❌ No, su estado es local por cada uso |
¿Necesita useContext ? | ✅ Sí, para acceder al valor del contexto | 🔄 Puede usar useContext , useState , etc. |
Ejemplo común | Tema, usuario logeado, idioma, carrito | useForm , useToggle , useAuth() |
¿Usan useState adentro? | 🔄 No necesariamente (el Provider lo maneja) | ✅ Muy común — pero crea nuevo estado en cada llamada |
¿Se pueden usar juntos? | ✅ ¡Sí! Es la práctica recomendada | ✅ Especialmente para acceder a contextos |
Práctica Clerk Auth (Próximamente...)
- https://clerk.com/docs/quickstarts/react
- https://clerk.com/docs/hooks/use-user
- https://home.openweathermap.org/api_keys
npm install @clerk/clerk-react