Saltar al contenido principal
Logo de JSCamp

Optimizando el código del Contexto

Aprende a mejorar el rendimiento y la experiencia de desarrollo creando un custom hook para consumir el contexto sin repetir lógica y añadiendo validaciones útiles.

midudev

Más clases y directos en YouTube

Suscribirse

Optimizando el código del Contexto

En la clase anterior vimos cómo implementar React Context para gestionar estado global. Ahora vamos a mejorar tanto el rendimiento como la experiencia de desarrollo aplicando un patrón profesional: crear un custom hook que encapsule toda la lógica de consumo del contexto.

El problema: demasiado useContext por todos lados

Actualmente, cada componente que necesita información del contexto hace algo así:

import { useContext } from 'react'import { AuthContext } from '../context/authContext'function Header() {  const { isLogin, login, logout } = useContext(AuthContext)  // ...}

Y lo mismo en cada componente:

import { useContext } from 'react'import { AuthContext } from '../context/authContext'function JobDetailPage() {  const { isLogin } = useContext(AuthContext)  // ...}

¿Cuál es el problema?

PROBLEMAS CON useContext DIRECTO─────────────────────────────────❌ Repetición de código   → Importar useContext + AuthContext en cada archivo❌ Acoplamiento a la implementación   → Todos los componentes conocen el nombre interno del contexto❌ Sin validación   → Si usas el contexto fuera del Provider, obtienes undefined❌ Peor autocompletado   → El IDE no puede inferir tan bien los tipos❌ Difícil de refactorizar   → Cambiar el nombre del contexto requiere actualizar N archivos

Esto funciona, pero es repetitivo, ruidoso y expone la implementación interna del contexto a todos los componentes.


La solución: crear un custom hook

La mejor práctica es crear un custom hook que centralice toda la lógica de acceso al contexto.

Paso 1: Crear el custom hook

Vamos a authContext.jsx y añadimos un nuevo hook personalizado:

import { createContext, useContext, useState } from 'react'export const AuthContext = createContext()// ✨ Custom hook para consumir el contextoexport function useAuth() {  const context = useContext(AuthContext)  if (context === undefined) {    throw new Error('useAuth debe usarse dentro de un AuthProvider')  }  return context}export function AuthProvider({ children }) {  const [isLogin, setIsLogin] = useState(false)  function login() {    setIsLogin(true)  }  function logout() {    setIsLogin(false)  }  const value = { isLogin, login, logout }  return <AuthContext value={value}>{children}</AuthContext>}

Anatomía del custom hook

useAuth()[1] Llama a useContext(AuthContext)[2] Valida que context !== undefined    ├─ Si es undefined → Error claro ❌    |  "useAuth debe usarse dentro de un AuthProvider"    |    └─ Si existe → Devuelve el contexto ✅       { isLogin, login, logout }

¿Qué hace este hook?

  1. Encapsula useContext: Ya no necesitas importarlo manualmente
  2. Valida el uso correcto: Si intentas usar useAuth fuera del Provider, obtienes un error claro
  3. Mejora el autocompletado: El IDE puede inferir mejor los tipos
  4. Centraliza la lógica: Un solo punto de entrada al contexto

Paso 2: Usar el custom hook en los componentes

Ahora, en lugar de importar useContext y AuthContext, simplemente importamos nuestro hook:

Antes (con useContext directo)

import { useContext } from 'react'import { AuthContext } from '../context/authContext'function Header() {  const { isLogin, login, logout } = useContext(AuthContext)  return <header>{/* ... */}</header>}

Después (con custom hook)

import { useAuth } from '../context/authContext'function Header() {  const { isLogin, login, logout } = useAuth()  return <header>{/* ... */}</header>}

Comparación visual

ANTES                              DESPUÉS─────────────────                  ─────────────────import { useContext }              import { useAuth }import { AuthContext }useContext(AuthContext)            useAuth()2 imports                          1 importExpone implementación              Oculta implementaciónSin validación                     Con validación

Ventajas del custom hook

1. Menos código repetitivo

Antes:

// Componente Aimport { useContext } from 'react'import { AuthContext } from '../context/authContext'const auth = useContext(AuthContext)// Componente Bimport { useContext } from 'react'import { AuthContext } from '../context/authContext'const auth = useContext(AuthContext)// Componente Cimport { useContext } from 'react'import { AuthContext } from '../context/authContext'const auth = useContext(AuthContext)

Después:

// Componente Aimport { useAuth } from '../context/authContext'const auth = useAuth()// Componente Bimport { useAuth } from '../context/authContext'const auth = useAuth()// Componente Cimport { useAuth } from '../context/authContext'const auth = useAuth()

2. Validación automática

Si intentas usar el hook fuera del Provider:

function ComponenteFueraDelProvider() {  const auth = useAuth() // ❌ Error claro y útil  // Error: useAuth debe usarse dentro de un AuthProvider}

Sin el custom hook, obtendrías:

const auth = useContext(AuthContext) // undefinedauth.login() // ❌ Cannot read property 'login' of undefined

3. Mejor experiencia de desarrollo

VENTAJAS PARA EL DESARROLLADOR──────────────────────────────✅ Autocompletado mejorado   → El IDE sugiere { isLogin, login, logout }✅ Errores más claros   → Mensaje específico si lo usas mal✅ Menos imports   → Solo necesitas importar useAuth✅ Abstracción limpia   → No necesitas saber que usa Context internamente✅ Fácil de refactorizar   → Cambiar la implementación no afecta a los componentes

4. Facilita el refactoring

Si en el futuro decides cambiar de Context a Zustand, Redux o cualquier otra solución, solo tienes que modificar el hook:

// Cambiar de Context a Zustandexport function useAuth() {  // Antes: return useContext(AuthContext)  // Después: return useAuthStore()  return useAuthStore()}

Los componentes que usan useAuth() no necesitan cambiar nada. ✨


Aplicando el patrón en toda la app

Ahora actualizamos todos los componentes que consumen el contexto:

En el Header

import { useAuth } from '../context/authContext'export function Header() {  const { isLogin, login, logout } = useAuth()  return (    <header>      <Logo />      <Nav />      {isLogin ? (        <button onClick={logout}>Cerrar sesión</button>      ) : (        <button onClick={login}>Iniciar sesión</button>      )}    </header>  )}

En JobDetailPage

import { useAuth } from '../context/authContext'export function JobDetailPage() {  const { isLogin } = useAuth()  return <div>{isLogin ? <ApplyButton /> : <p>Inicia sesión para aplicar</p>}</div>}

En cualquier otro componente

import { useAuth } from '../context/authContext'export function UserProfile() {  const { isLogin, logout } = useAuth()  if (!isLogin) return <Redirect to="/login" />  return <div>Perfil de usuario</div>}

Patrón completo: Context + Custom Hook

Aquí está el patrón completo que deberías seguir siempre que crees un contexto:

import { createContext, useContext, useState } from 'react'// 1. Crear el contextoexport const AuthContext = createContext()// 2. Crear el custom hook con validaciónexport function useAuth() {  const context = useContext(AuthContext)  if (context === undefined) {    throw new Error('useAuth debe usarse dentro de un AuthProvider')  }  return context}// 3. Crear el Providerexport function AuthProvider({ children }) {  const [isLogin, setIsLogin] = useState(false)  const login = () => setIsLogin(true)  const logout = () => setIsLogin(false)  const value = { isLogin, login, logout }  return <AuthContext value={value}>{children}</AuthContext>}

Estructura de archivos recomendada

/src  /context    authContext.jsx       ← Context + Provider + Custom Hook  /components    Header.jsx            ← Usa useAuth()    JobDetailPage.jsx     ← Usa useAuth()    UserProfile.jsx       ← Usa useAuth()

Comparación final: Antes vs Después

Código en los componentes

ANTES (sin custom hook):

// 😕 Muchos importsimport { useContext } from 'react'import { AuthContext } from '../context/authContext'function MyComponent() {  // 😕 Sintaxis verbosa  const { isLogin } = useContext(AuthContext)  // 😕 Sin validación  // Si AuthContext es undefined → error críptico  return <div>{isLogin ? 'Logged' : 'Guest'}</div>}

DESPUÉS (con custom hook):

// ✅ Un solo importimport { useAuth } from '../context/authContext'function MyComponent() {  // ✅ Sintaxis limpia  const { isLogin } = useAuth()  // ✅ Con validación automática  // Si se usa mal → error claro  return <div>{isLogin ? 'Logged' : 'Guest'}</div>}

Tabla comparativa

Aspecto useContext directo Custom Hook
Imports necesarios 2 (useContext + Context) 1 (useAuth)
Validación ❌ No ✅ Sí
Mensaje de error Críptico Claro y útil
Autocompletado Regular Excelente
Acoplamiento Alto Bajo
Facilidad de refactoring Difícil Fácil
Código repetitivo Mucho Mínimo

Buenas prácticas con custom hooks de contexto

1. Nombra el hook según el contexto

// ✅ Buenoexport function useAuth() { ... }export function useTheme() { ... }export function useCart() { ... }// ❌ Maloexport function useContext() { ... }export function useData() { ... }export function useStuff() { ... }

2. Siempre valida el contexto

// ✅ Buenoexport function useAuth() {  const context = useContext(AuthContext)  if (context === undefined) {    throw new Error('useAuth debe usarse dentro de un AuthProvider')  }  return context}// ❌ Malo (sin validación)export function useAuth() {  return useContext(AuthContext) // Puede ser undefined}

3. Exporta solo lo necesario

// ✅ Buenoexport function useAuth() { ... }export function AuthProvider({ children }) { ... }// No exportes AuthContext// ❌ Maloexport const AuthContext = createContext() // Expone implementaciónexport function useAuth() { ... }export function AuthProvider({ children }) { ... }

4. Documenta el hook

/** * Hook para acceder al contexto de autenticación * * @returns {Object} Objeto con isLogin, login y logout * @throws {Error} Si se usa fuera de AuthProvider * * @example * function MyComponent() { *   const { isLogin, login, logout } = useAuth() *   return <button onClick={login}>Login</button> * } */export function useAuth() {  // ...}

Resumen de la clase

En esta clase has aprendido:

  1. El problema con useContext directo: Código repetitivo y sin validación
  2. Cómo crear un custom hook: Encapsular useContext en una función reutilizable
  3. Validación automática: Detectar uso incorrecto del contexto con mensajes claros
  4. Ventajas del patrón: Menos código, mejor DX, más fácil de mantener
  5. Buenas prácticas: Nombrado, validación, exports y documentación

Conclusión

Crear un custom hook para cada contexto es una best practice profesional que:

  • ✅ Reduce código repetitivo
  • ✅ Mejora la experiencia de desarrollo
  • ✅ Facilita el mantenimiento y refactoring
  • ✅ Añade validaciones útiles
  • ✅ Oculta detalles de implementación

Regla de oro: Siempre que crees un Context, crea también su custom hook. Tu yo del futuro te lo agradecerá.

En la siguiente clase veremos cómo llevar la gestión de estado global al siguiente nivel con Zustand, una librería que soluciona las limitaciones de rendimiento de Context manteniendo una API simple y elegante.