Especialização Frontend com React & TypeScript • Demonstrações Práticas Executáveis


🎯 Contexto e Objetivo Prático

Criação de Context API modular com Provider fortemente tipado e Custom Hook defensivo para controle global de tema.


💻 Código Fonte React 19 (TSX)

// ThemeContext.tsx
import React, { createContext, useContext, useState, useEffect } from "react";

export type ThemeMode = "light" | "dark" | "system";

export interface ThemeContextValue {
  theme: ThemeMode;
  resolvedTheme: "light" | "dark";
  setTheme: (theme: ThemeMode) => void;
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextValue | null>(null);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [theme, setTheme] = useState<ThemeMode>("system");

  const resolvedTheme =
    theme === "system"
      ? typeof window !== "undefined" && window.matchMedia("(prefers-color-scheme: dark)").matches
        ? "dark"
        : "light"
      : theme;

  const toggleTheme = () => {
    setTheme(prev => (prev === "dark" ? "light" : "dark"));
  };

  const value: ThemeContextValue = {
    theme,
    resolvedTheme,
    setTheme,
    toggleTheme
  };

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

// Custom Hook Defensivo: Garante consumo estrito dentro do Provider
export function useTheme(): ThemeContextValue {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error("useTheme() deve ser executado obrigatoriamente dentro de um <ThemeProvider>.");
  }
  return context;
}

// Componente Consumidor
export function ThemeToggleButton() {
  const { resolvedTheme, toggleTheme } = useTheme();

  return (
    <button
      onClick={toggleTheme}
      className="px-3 py-1.5 rounded-lg border text-xs font-semibold"
    >
      Tema Atual: {resolvedTheme.toUpperCase()} (Clique para alternar)
    </button>
  );
}

🖥️ Saída de Renderização Visual e Comportamento

[ThemeProvider Ativo]
Tema Resolvido: DARK
[Tema Atual: DARK (Clique para alternar)]
(Se invocado fora do Provider, dispara TypeError defensivo com mensagem clara)