✨ ¡Excelente momento para conectar! Estoy disponible para tu próximo proyecto. No más esperas ni complicaciones. Te ofrezco desarrollo ágil, comunicación clara y resultados que superan expectativas. Conversemos sobre cómo puedo impulsar tu idea.

Guía Completa para Dominar el Hook useContext en React

¿Qué esel Hook useContext?

useContext es un Hook de React que permite suscribirse y consumir datos desde un Context Provider en cualquier nivel del árbol de componentes. Es la solución de React para evitar el “prop drilling” (pasar props a través de múltiples niveles de componentes).

  Características principales

  • 🌍 Compartir estado global
  • ⚡ Acceso a datos desde cualquier componente hijo
  • 🎯 Evita prop drilling
  • 🔄 Actualización automática de componentes

Sintaxis Básica

jsx
// 1. Crear el contexto
const MiContexto = createContext(valorInicial);

// 2. Proveer el contexto
<MiContexto.Provider value={valor}>
{children}
</MiContexto.Provider>

// 3. Consumir el contexto
const valor = useContext(MiContexto);

Ejemplo Completo de Implementación


1. Creación y Configuración del Contexto

jsx
// ThemeContext.js
import { createContext, useState } from 'react';

export const ThemeContext = createContext();

export function ThemeProvider({ children }) {
const [theme, setTheme] = useState('light');

const toggleTheme = () => {
  setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};

const value = {
  theme,
  toggleTheme
};

return (
  <ThemeContext.Provider value={value}>
    {children}
  </ThemeContext.Provider>
);
}

2. Uso en Componentes

jsx
// App.js
function App() {
return (
  <ThemeProvider>
    <MainLayout />
  </ThemeProvider>
);
}

// MainLayout.js
function MainLayout() {
const { theme } = useContext(ThemeContext);

return (
  <div className={`layout ${theme}`}>
    <Navbar />
    <Content />
    <Footer />
  </div>
);
}

// ThemeToggle.js
function ThemeToggle() {
  const { theme, toggleTheme } = useContext(ThemeContext);
  
  return (
    <button onClick={toggleTheme}>
      Cambiar a tema {theme === 'light' ? 'oscuro' : 'claro'}
    </button>
  );
}

Casos de Uso Prácticos


1. Gestión de Autenticación

jsx
// AuthContext.js
import { createContext, useState, useContext } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(false);

  const login = async (credentials) => {
    setLoading(true);
    try {
      const response = await apiLogin(credentials);
      setUser(response.user);
      return true;
    } catch (error) {
      return false;
    } finally {
      setLoading(false);
    }
  };

  const logout = () => {
    setUser(null);
  };

  return (
    <AuthContext.Provider value={{ user, loading, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// Hook personalizado para usar el contexto
export function useAuth() {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth debe usarse dentro de un AuthProvider');
  }
  return context;
}

2. Gestión de Carrito de Compras

jsx
// CartContext.js
const CartContext = createContext();

export function CartProvider({ children }) {
  const [items, setItems] = useState([]);

  const addItem = (product) => {
    setItems(prevItems => {
      const existingItem = prevItems.find(item => item.id === product.id);
      if (existingItem) {
        return prevItems.map(item =>
          item.id === product.id
            ? { ...item, quantity: item.quantity + 1 }
            : item
        );
      }
      return [...prevItems, { ...product, quantity: 1 }];
    });
  };

  const removeItem = (productId) => {
    setItems(prevItems => 
      prevItems.filter(item => item.id !== productId)
    );
  };

  const clearCart = () => {
    setItems([]);
  };

  const getTotal = () => {
    return items.reduce((total, item) => 
      total + (item.price * item.quantity), 0
    );
  };

  return (
    <CartContext.Provider value={{
      items,
      addItem,
      removeItem,
      clearCart,
      getTotal
    }}>
      {children}
    </CartContext.Provider>
  );
}

3. Gestión de Idiomas (i18n)

jsx
// LanguageContext.js
const LanguageContext = createContext();

const translations = {
  es: {
    welcome: 'Bienvenido',
    goodbye: 'Adiós'
  },
  en: {
    welcome: 'Welcome',
    goodbye: 'Goodbye'
  }
};

export function LanguageProvider({ children }) {
  const [language, setLanguage] = useState('es');

  const translate = (key) => {
    return translations[language][key] || key;
  };

  return (
    <LanguageContext.Provider value={{
      language,
      setLanguage,
      translate
    }}>
      {children}
    </LanguageContext.Provider>
  );
}

Patrones Avanzados


1. Múltiples Contextos

jsx
function App() {
  return (
    <AuthProvider>
      <ThemeProvider>
        <LanguageProvider>
          <CartProvider>
            <MainApp />
          </CartProvider>
        </LanguageProvider>
      </ThemeProvider>
    </AuthProvider>
  );
}

2. Context con Reducer

jsx
const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function CountProvider({ children }) {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <CountContext.Provider value={{ state, dispatch }}>
      {children}
    </CountContext.Provider>
  );
}

Mejores Prácticas 🎯


1. Crear Hooks Personalizados

jsx
// useTheme.js
export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme debe usarse dentro de un ThemeProvider');
  }
  return context;
}

2. Separación de Contextos

jsx
// Mejor: Contextos separados para diferentes responsabilidades
const UserContext = createContext();
const ThemeContext = createContext();
const SettingsContext = createContext();

// Peor: Un solo contexto para todo
const AppContext = createContext();

3. Optimización de Renders

jsx
function OptimizedProvider({ children }) {
  const [state, setState] = useState({
    theme: 'light',
    user: null
  });

  const themeValue = useMemo(() => ({
    theme: state.theme,
    setTheme: (theme) => setState(prev => ({ ...prev, theme }))
  }), [state.theme]);

  const userValue = useMemo(() => ({
    user: state.user,
    setUser: (user) => setState(prev => ({ ...prev, user }))
  }), [state.user]);

  return (
    <ThemeContext.Provider value={themeValue}>
      <UserContext.Provider value={userValue}>
        {children}
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}

Errores Comunes y Soluciones


1. No Envolver el Provider

jsx
// ❌ Mal: Usar contexto sin provider
function App() {
  const theme = useContext(ThemeContext); // Error!
  return <div>{theme}</div>;
}

// ✅ Bien: Asegurar que existe un provider
function App() {
  return (
    <ThemeProvider>
      <ThemedComponent />
    </ThemeProvider>
  );
}

2. Re-renders Innecesarios

jsx
// ❌ Mal: Valor nuevo en cada render
function Provider({ children }) {
  return (
    <MyContext.Provider value={{ data: {} }}>
      {children}
    </MyContext.Provider>
  );
}

// ✅ Bien: Valor memorizado
function Provider({ children }) {
  const value = useMemo(() => ({
    data: {}
  }), []);

  return (
    <MyContext.Provider value={value}>
      {children}
    </MyContext.Provider>
  );
}

Debugging

1. DevTools

jsx
function DebugProvider({ children }) {
  const value = useContext(MyContext);
  
  useEffect(() => {
    console.log('Context value changed:', value);
  }, [value]);

  return children;
}

2. Error Boundaries para Contexto

jsx
class ContextErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <h1>Error en el contexto</h1>;
    }
    return this.props.children;
  }
}