Skip to content

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 contenidochildren es el patrón ideal para componentes que actúan como contenedores genéricos (layouts, modales, tarjetas, etc.)
✅ Para evitar props innecesariasEn lugar de definir props como content, body, mainContent, etc., usas solo children.
✅ Convención de ReactEs una práctica común y conocida por todos los desarrolladores React.
tsx
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;
tsx
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:

tsx
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;
tsx
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:

tsx
<IdiomaProvider>
  <App /> ← Todos los componentes dentro pueden acceder al idioma global
</IdiomaProvider>

Y dentro de cualquier componente puedes acceder a ese valor con:

tsx
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

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.
  • ¿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.
  • ¿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

tsx
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

tsx
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

tsx
export interface User {
  name: string;
  email: string;
}

src\context\auth\auth-provider.tsx

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

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

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

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

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 globalesFunció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únTema, usuario logeado, idioma, carritouseForm, 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...)

sh
npm install @clerk/clerk-react