Context API + Hooks + Expo Router

Objetivos da Aula

Ao final desta sessão, você será capaz de:

  • Identificar e curar o "Prop Drilling".
  • Centralizar o controle da aplicação com Context API.
  • Criar abstrações inteligentes com Hooks personalizados.
  • Dominar o Roteamento Nativo com Expo Router (File-based).
  • Blindar sua aplicação com Guards de Rotas Privadas.

O Problema: Prop Drilling

A Analogia do "Telefone Sem Fio"

Imagine um prédio de 10 andares sem elevador. Para entregar uma encomenda no 10º andar, o entregador passa para o porteiro, que passa para o síndico, que passa para o vizinho...

No código: Você passa uma prop por 5 componentes que não a utilizam, apenas para que o último da lista receba o dado.

  • Risco: Se um "meio de campo" mudar, a corrente quebra.
  • Custo: Manutenção difícil e código sujo.

A Solução: Context API

A Analogia da "Caixa d'Água"

Em vez de carregar baldes escada acima, instalamos uma Caixa d'Água (Context) no topo do prédio.

Qualquer apartamento (componente), independente do andar, só precisa instalar uma torneira (Hook) para acessar a água.

Exemplos de uso global:

  • 👤 Dados do Usuário (Sessão)
  • 🌓 Tema (Light/Dark Mode)
  • 🛒 Carrinho de Compras
  • 🌐 Idioma (i18n)

1️⃣ Criando o Contexto (A Definição)

O contexto é apenas a "assinatura" do que será compartilhado.

// src/contexts/AuthContext.js
import { createContext } from "react";

// Criamos o contexto vazio (será preenchido pelo Provider)
export const AuthContext = createContext({});

2️⃣ Criando o Provider (A Central de Controle)

O Provider é o componente que "envelopa" a lógica e distribui os dados.

export function AuthProvider({ children }) {
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(true); // Vital para UX!

  // Simula verificação de login ao abrir o app
  useEffect(() => {
    setTimeout(() => setIsLoading(false), 2000); 
  }, []);

  const login = (email) => setUser({ email, name: "Allan Silva" });
  const logout = () => setUser(null);

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

3️⃣ Hook Personalizado (A Torneira)

Por que criar um Hook? Para não precisar importar useContext e AuthContext em toda tela. Facilitamos a vida do desenvolvedor.

// src/hooks/useAuth.js
import { useContext } from "react";
import { AuthContext } from "../contexts/AuthContext";

export function useAuth() {
  const context = useContext(AuthContext);
  
  if (!context) {
    throw new Error("useAuth deve ser usado dentro de um AuthProvider");
  }

  return context;
}

Estrutura Profissional de Pastas

Organização em Camadas (Separation of Concerns)

src
 ├── app (Rotas e Telas - O que o usuário vê)
 ├── components (Peças reutilizáveis)
 ├── contexts (Estado Global - A inteligência)
 ├── hooks (Atalhos para lógica)
 └── services (Comunicação com o mundo externo/API)

Dica: Manter a lógica de autenticação no context e a navegação no app separa a "Regra de Negócio" da "Interface".

Expo Router: Navegação Moderna

O Expo Router utiliza o sistema de arquivos. Pasta = Rota.

  • app/_layout.js: O "Pai" de todos. Define quem envolve a aplicação.
  • app/(tabs)/: O parênteses indica que a pasta não aparece na URL/Caminho, serve apenas para agrupar o Layout de abas.

Rotas Privadas: O Segurança da Balada

Não basta esconder o botão de "Perfil". Se o usuário digitar o caminho, ele não pode entrar sem "pulseira" (token).

// src/components/PrivateRoute.js
import { Redirect } from "expo-router";
import { useAuth } from "../hooks/useAuth";
import { ActivityIndicator, View } from "react-native";

export default function PrivateRoute({ children }) {
  const { user, isLoading } = useAuth();

  if (isLoading) {
    return (
      <View style={{ flex: 1, justifyContent: 'center' }}>
        <ActivityIndicator size="large" color="#0000ff" />
      </View>
    );
  }

  return user ? children : <Redirect href="/login" />;
}

Implementando no Layout Principal

// app/_layout.js
import { Stack } from "expo-router";
import { AuthProvider } from "../src/contexts/AuthContext";

export default function RootLayout() {
  return (
    <AuthProvider>
      <Stack screenOptions={{ headerShown: false }}>
        <Stack.Screen name="login" />
        <Stack.Screen name="(tabs)" />
      </Stack>
    </AuthProvider>
  );
}

Prática: Tela de Login

import { useAuth } from "../hooks/useAuth";
import { Button, View, Text } from "react-native";
import { useRouter } from "expo-router";

export default function Login() {
  const { login } = useAuth();
  const router = useRouter();

  function handleLogin() {
    login("allan@email.com");
    router.replace("/(tabs)"); // Substitui a rota para não voltar ao login
  }

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <Text>Bem-vindo ao App Profissional</Text>
      <Button title="Entrar no Sistema" onPress={handleLogin} />
    </View>
  );
}

Desafio Prático 🚀

Sua missão:

  1. Configure o AuthContext para salvar o nome do usuário.
  2. Na tela de Perfil, exiba o nome que vem do contexto.
  3. Crie um botão de Logout que limpa o estado e manda o usuário de volta para a tela de Login.

Extra de Ouro:
Use o AsyncStorage dentro do AuthProvider para que, ao fechar e abrir o app, o usuário continue logado!

Conclusão

  • Context API não é apenas para login, é para qualquer dado "onipresente".
  • Hooks limpam seu código e escondem a complexidade.
  • Expo Router aproxima o desenvolvimento mobile da simplicidade da Web.