---
# System prepended metadata

title: Context Api
tags: [Frontend]

---

# Context Api

O Context API do React é uma solução nativa para compartilhar dados de maneira eficiente entre componentes sem a necessidade de passar props manualmente por várias camadas de componentes. Ele é útil especialmente quando certos dados ou estados precisam ser acessados em vários níveis da árvore de componentes, evitando o "prop drilling", que é o ato de passar dados por vários componentes intermediários desnecessariamente.

**Como funciona:**

* **Criação do Contexto**: Um contexto é criado usando a função `createContext`, o que gera um par de componentes: `Provider` e `Consumer`.
* **Provider**: O `Provider` é o componente responsável por "fornecer" os valores para todos os componentes que "consomem" aquele contexto. Ele envolve os componentes que devem acessar esses valores.
* **Consumer**: O `Consumer` pode ser usado para acessar os dados fornecidos pelo `Provider`. No entanto, o uso de `useContext`, um hook, é mais comum e simplificado.

Exemplo:

```typescript!
// context/authContext.tsx

import React, { createContext, useState, useContext } from 'react';

// 1. Criando o contexto
const AuthContext = createContext();

// 2. Criando o Provider
export const AuthProvider = ({ children }) => {
  const [isLoggedIn, setIsLoggedIn] = useState(false);

  return (
    <AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
      {children}
    </AuthContext.Provider>
  );
};

// 3. Usando o contexto em outro componente
export const useAuth = () => useContext(AuthContext);

```

**Quando Usar o Context API?**

* Tema (dark mode/light mode): Troca de temas de uma aplicação.
* Autenticação: Compartilhar informações sobre o usuário logado entre várias páginas.
* Preferências globais: Idiomas, configurações de usuários, etc.
* Carrinho de compras: Manter o estado do carrinho de compras em um e-commerce acessível em várias páginas.

## Async Storage

O AsyncStorage é uma solução de armazenamento simples e assíncrona para dados persistentes no dispositivo. Ele é ideal para armazenar pequenas quantidades de dados, como tokens de autenticação ou preferências do usuário. O AsyncStorage armazena os dados no armazenamento interno do dispositivo, e esses dados permanecem disponíveis mesmo após o fechamento do app.

**Como Funciona:**

* **Gravando Dados**: Você pode salvar dados no formato chave-valor. Os valores são armazenados como strings.
* **Lendo Dados**: Os dados armazenados podem ser recuperados posteriormente, e, como o nome sugere, essas operações são assíncronas.
* **Removendo Dados**: Também é possível remover dados específicos ou limpar todo o armazenamento.

**Exemplo**

```typescript
import AsyncStorage from '@react-native-async-storage/async-storage';

// 1. Salvando um valor
const storeData = async (value) => {
  try {
    await AsyncStorage.setItem('@storage_Key', value);
  } catch (e) {
    // Error saving data
  }
};

// 2. Lendo um valor
const getData = async () => {
  try {
    const value = await AsyncStorage.getItem('@storage_Key');
    if(value !== null) {
      // Value is retrieved
    }
  } catch(e) {
    // Error reading value
  }
};

// 3. Removendo um valor
const removeData = async () => {
  try {
    await AsyncStorage.removeItem('@storage_Key');
  } catch(e) {
    // Error removing value
  }
};

```

# Passo a Passo

## AuthContext

Criando um authcontext para realizar o login e salvar os dados do usuario e o token com asyncStorage

### **authContext**

```typescript!
import {
  createContext,
  ReactNode,
  useContext,
  useEffect,
  useState,
} from "react";
import AsyncStorage from "@react-native-async-storage/async-storage";
import axios from "axios";
import { User } from "../types/modelTypes";

interface AuthContextData {
  user: User | null;
  signIn: (email: string, password: string) => Promise<void>;
  signOut: () => Promise<void>;
  signed: boolean;
  loading: boolean;
}

// Define o tipo dos props do <Provider></Provider>
interface AuthProviderProps {
  children: ReactNode;
}

// Cria o contexto com um objeto vazio tipado
export const AuthContext = createContext<AuthContextData>(
  {} as AuthContextData
);

export function AuthProvider({ children }: AuthProviderProps) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true); // Para indicar quando está carregando

  // Carrega o token e os dados do usuário do AsyncStorage ao iniciar o app
  useEffect(() => {
    async function loadStorageData() {
      const storedUser = await AsyncStorage.getItem("@Auth:user");
      const storedToken = await AsyncStorage.getItem("@Auth:token");

      if (storedUser && storedToken) {
        setUser(JSON.parse(storedUser));
        axios.defaults.headers.common[
          "Authorization"
        ] = `Bearer ${storedToken}`; // Define o token como padrão nas requisições
      }
      setLoading(false); // Carregamento finalizado
    }

    loadStorageData();
  }, []);

  const signIn = async (email: string, password: string) => {
    try {
      // Faz a requisição de login para o backend
      const response = await axios.post("http://rotaApi:3000/auth/login", {
        email,
        password,
      });
      const { access_token, user } = response.data;

      // Salva os dados no AsyncStorage
      await AsyncStorage.setItem("@Auth:user", JSON.stringify(user));
      await AsyncStorage.setItem("@Auth:token", access_token);

      // Define o token para as futuras requisições
      axios.defaults.headers.common["Authorization"] = `Bearer ${access_token}`;

      // Atualiza o estado
      setUser(user);
    } catch (error) {
      console.error("Erro durante o login:", error);
      throw new Error("Falha na autenticação.");
    }
  };

  const signOut = async () => {
    // Remove os dados do AsyncStorage
    await AsyncStorage.removeItem("@Auth:user");
    await AsyncStorage.removeItem("@Auth:token");

    // Reseta o estado
    setUser(null);
  };

  return (
    <AuthContext.Provider
      value={{ user, signIn, signOut, signed: !!user, loading }}
    >
      {children}
    </AuthContext.Provider>
  );
}

// Hook personalizado para usar o contexto
export function useAuth(): AuthContextData {
  const context = useContext(AuthContext);

  if (!context) {
    throw new Error("useAuth deve ser usado dentro de um AuthProvider");
  }

  return context;
}

```

- `AuthContextData` Interface:

Define os dados e funções disponíveis no contexto de autenticação: o usuário logado, funções de login (signIn) e logout (signOut), além de flags como signed (indica se o usuário está autenticado) e loading (indica se o app está carregando as informações de autenticação).

- `AuthProvider` Componente:

* **Estado interno**:
    * user: Armazena os dados do usuário logado.
    * loading: Indica se os dados de autenticação estão sendo carregados.
* **useEffect**:
    * Ao iniciar o app, carrega o token e os dados do usuário do AsyncStorage e define o cabeçalho padrão de autenticação no Axios.
* **signIn** função:
    * Realiza uma requisição de login para o backend com as credenciais do usuário.
    * Armazena o token e os dados do usuário no AsyncStorage e configura o Axios para enviar o token em todas as requisições futuras.
* **signOut** função:
    * Remove o token e os dados do usuário do AsyncStorage e reseta o estado local do usuário.

### Página de Login

```typescript
import { View, Text, TextInput, TouchableOpacity, Alert } from "react-native";
import { useState } from "react";
import { router } from "expo-router";
import { useAuth } from "../context/authContext";

export default function Login() {
  const { signIn } = useAuth(); // Usando a função signIn do contexto de autenticação
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  async function handleLogin() {
    if (email && password) {
      try {
        // Chama a função signIn e aguarda o login
        await signIn(email, password);
        router.replace("/home"); // Redireciona para a página inicial após login bem-sucedido
      } catch (error) {
        Alert.alert("Erro", "Credenciais inválidas!"); // Mostra um alerta em caso de erro
      }
    } else {
      Alert.alert("Erro", "Preencha todos os campos!"); // Verifica se ambos campos foram preenchidos
    }
  }
    
    // Código de formulário + Botão para chamar handleLogin
```

### Layout

Necessários adicionar ao rootlayout ou algum arquivo de entrada principal (camada mais externa)

```typescript
<AuthProvider>
          <Stack>
            <Stack.Screen name="index" options={{ title: "Login" }} />
            <Stack.Screen name="home/index" options={{ title: "Home" }} />
            <Stack.Screen name="pedidos/index" options={{ title: "Pedidos" }} />
          </Stack>
</AuthProvider>
```

## ThemeContext

Este código encapsula a lógica de alternância de tema (claro/escuro) em um contexto reutilizável. O ThemeProvider provê o estado do tema e a função de alternância para todos os componentes dentro de sua árvore. Com isso, qualquer componente pode acessar e manipular o tema chamando o hook useTheme, sem necessidade de passar props explicitamente.

`ThemeContextData` Interface:

* Define os dados e funções disponíveis no contexto de tema:
* isDarkMode: Um booleano que indica se o tema escuro está ativado.
* toggleTheme: Função que alterna entre o tema claro e o tema escuro.

`ThemeProvider` Componente:

* **Estado interno**:
    * isDarkMode: Controla se o tema atual é escuro (true) ou claro (false). Usa o useState para manter o valor.
* Função toggleTheme:
    * Inverte o valor atual de isDarkMode, alternando entre os dois temas.

### themeContext

```typescript
// src/contexts/ThemeContext.tsx
import { createContext, useContext, useState, ReactNode } from "react";

// Interface que define o formato dos dados do contexto
interface ThemeContextData {
  isDarkMode: boolean;   // Estado que indica se o tema escuro está ativado
  toggleTheme: () => void; // Função que alterna entre tema claro/escuro
}

// Interface para os props do ThemeProvider
interface ThemeProviderProps {
  children: ReactNode; // Tipagem para os componentes filhos que o ThemeProvider vai envolver
}

// Cria o contexto ThemeContext com um valor inicial vazio, tipado com ThemeContextData
export const ThemeContext = createContext<ThemeContextData>({} as ThemeContextData);

// Componente ThemeProvider que será usado para fornecer o contexto aos componentes filhos
export function ThemeProvider({ children }: ThemeProviderProps) {
  // Define o estado para controlar o modo do tema (escuro/claro)
  const [isDarkMode, setIsDarkMode] = useState(false); // Inicia o tema como claro (false)

  // Função para alternar entre o modo escuro e claro
  const toggleTheme = () => {
    setIsDarkMode(!isDarkMode); // Inverte o valor atual de isDarkMode
  };

  // Retorna o provedor do contexto com os valores de isDarkMode e toggleTheme
  return (
    <ThemeContext.Provider value={{ isDarkMode, toggleTheme }}>
      {children} {/* Renderiza os componentes filhos envolvidos */}
    </ThemeContext.Provider>
  );
}

// Hook personalizado para consumir o contexto de tema de forma simplificada
export function useTheme(): ThemeContextData {
  // Usa o hook useContext para acessar o contexto ThemeContext
  const context = useContext(ThemeContext);

  // Lança um erro se o hook for usado fora do ThemeProvider
  if (!context) {
    throw new Error('useTheme deve ser usado dentro de um ThemeProvider');
  }

  // Retorna o contexto para ser usado nos componentes que chamarem o hook
  return context;
}
```

### Página Home

Página Home utilizando o booleano do modo escuro para trocar a estilização do tailwind

```typescript
// app/home.tsx
import { View, Text, TouchableOpacity } from 'react-native';
import { styled } from 'nativewind';
import { useAuth } from '../../context/authContext';
import { useTheme } from '../../context/themeContext';
import { router } from 'expo-router';

const StyledView = styled(View);
const StyledText = styled(Text);
const StyledTouchableOpacity = styled(TouchableOpacity);

export default function Home() {
  const { isDarkMode, toggleTheme } = useTheme();
  const { user, signOut } = useAuth();

  function handleLogout() {
    signOut();
    router.replace("/");
  }

  return (
    <StyledView 
      className={`flex-1 p-5 
      ${isDarkMode ? 'bg-gray-900' : 'bg-white'}`}
    >
      <StyledView 
        className={`p-4 rounded-lg mb-6 
        ${isDarkMode ? 'bg-gray-800' : 'bg-gray-100'}`}
      >
        <StyledText 
          className={`text-2xl font-bold mb-4 
          ${isDarkMode ? 'text-white' : 'text-gray-900'}`}
        >
          Bem vindo(a)!
        </StyledText>
        
        <StyledText 
          className={`text-lg mb-2 
          ${isDarkMode ? 'text-gray-200' : 'text-gray-700'}`}
        >
          Nome: {user?.name}
        </StyledText>
        
        <StyledText 
          className={`text-lg 
          ${isDarkMode ? 'text-gray-200' : 'text-gray-700'}`}
        >
          Email: {user?.email}
        </StyledText>
      </StyledView>

      <StyledTouchableOpacity
        className="w-full h-12 bg-secondary rounded-lg justify-center items-center mb-4"
        onPress={toggleTheme}
      >
        <StyledText className={`rounded text-lg font-semibold p-3 ${isDarkMode ? 'bg-white text-gray-700' :  'bg-gray-700 text-white'}`}>
          {isDarkMode ? 'Modo Claro' : 'Modo Escuro'}
        </StyledText>
      </StyledTouchableOpacity>

      <StyledTouchableOpacity
        className="w-full h-12 bg-red-600 rounded-lg justify-center items-center"
        onPress={handleLogout}
      >
        <StyledText className="text-white text-lg font-semibold">
          Sair
        </StyledText>
      </StyledTouchableOpacity>

      <StyledTouchableOpacity
        className="w-full h-12 bg-red-600 rounded-lg justify-center items-center"
        onPress={() => router.push("/setting")}
      >
        <StyledText className="text-white text-lg font-semibold">
          Configurações
        </StyledText>
      </StyledTouchableOpacity>
    </StyledView>
  );
}
```

## LanguageContext

Esse código implementa um contexto de idioma para um aplicativo React Native, permitindo mudar o idioma dinamicamente, buscar traduções, e salvar a preferência do usuário no armazenamento local para que seja persistida mesmo após fechar o aplicativo. A recomendação é o i18n porém funciona também

### Pasta locales

Pasta contendo arquivos de traduções em formato chave-valor

```typescript
// src/locales/pt.ts
export default {
    auth: {
      welcome: "Seja bem-vindo(a)!",
      login: "Entrar",
      logout: "Sair",
      email: "Digite seu email",
      password: "Digite sua senha",
      emailRequired: "Email é obrigatório",
      passwordRequired: "Senha é obrigatória",
    },
    home: {
      title: "Página Inicial",
      hello: "Olá",
      profile: "Perfil",
      name: "Nome",
      email: "Email",
      plan: "Plano",
    },
    settings: {
      title: "Configurações",
      language: "Idioma",
      theme: "Tema",
      darkMode: "Modo Escuro",
      lightMode: "Modo Claro",
    },
    common: {
      loading: "Carregando...",
      error: "Ocorreu um erro",
      success: "Sucesso!",
      save: "Salvar",
      cancel: "Cancelar",
      confirm: "Confirmar",
    },
  };
```

```typescript
// src/locales/en.ts
export default {
    auth: {
      welcome: "Welcome!",
      login: "Login",
      logout: "Logout",
      email: "Enter your email",
      password: "Enter your password",
      emailRequired: "Email is required",
      passwordRequired: "Password is required",
    },
    home: {
      title: "Home",
      hello: "Hello",
      profile: "Profile",
      name: "Name",
      email: "Email",
      plan: "Plan",
    },
    settings: {
      title: "Settings",
      language: "Language",
      theme: "Theme",
      darkMode: "Dark Mode",
      lightMode: "Light Mode",
    },
    common: {
      loading: "Loading...",
      error: "An error occurred",
      success: "Success!",
      save: "Save",
      cancel: "Cancel",
      confirm: "Confirm",
    },
  };
```

### LanguageContext

```typescript
import { createContext, useContext, useState, ReactNode, useEffect } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import pt from '../locales/pt'; // Importa as traduções em Português
import en from '../locales/en'; // Importa as traduções em Inglês
import es from '../locales/es'; // Importa as traduções em Espanhol

// Tipo para definir o formato das traduções (usando a estrutura do arquivo de tradução pt como referência)
type Translations = typeof pt;

// Interface para os dados que o contexto vai fornecer
interface LanguageContextData {
  language: string; // Idioma atual
  setLanguage: (lang: string) => Promise<void>; // Função para alterar o idioma
  translate: (key: string, section?: keyof Translations) => string; // Função de tradução
  availableLanguages: { code: string; name: string; }[]; // Lista de idiomas disponíveis
}

// Interface para as propriedades do provider (envolve os componentes filhos)
interface LanguageProviderProps {
  children: ReactNode;
}

// Definimos a chave para salvar o idioma no AsyncStorage
const LANGUAGE_STORAGE_KEY = '@MyApp:language';

// Mapa que contém todas as traduções disponíveis para os idiomas suportados
const translations = {
  pt, // Português
  en, // Inglês
  es, // Espanhol
};

// Lista de idiomas que podem ser selecionados no app
const availableLanguages = [
  { code: 'pt', name: 'Português' }, // Idioma: Português
  { code: 'en', name: 'English' },   // Idioma: Inglês
  { code: 'es', name: 'Español' },   // Idioma: Espanhol
];

// Criação do contexto com um valor inicial vazio (para ser preenchido mais tarde)
export const LanguageContext = createContext<LanguageContextData>({} as LanguageContextData);

// Função que define o LanguageProvider, envolvendo os componentes filhos e provendo o contexto
export function LanguageProvider({ children }: LanguageProviderProps) {
  // Estado inicial do idioma é "pt" (Português)
  const [language, setLanguageState] = useState('pt');

  // useEffect é usado para carregar o idioma salvo assim que o componente monta
  useEffect(() => {
    loadSavedLanguage();
  }, []);

  // Função que tenta carregar o idioma salvo do AsyncStorage
  async function loadSavedLanguage() {
    try {
      const savedLanguage = await AsyncStorage.getItem(LANGUAGE_STORAGE_KEY);
      if (savedLanguage) {
        setLanguageState(savedLanguage); // Se encontrar o idioma, define no estado
      }
    } catch (error) {
      console.error('Error loading language:', error); // Mostra erro no console se houver falha
    }
  }

  // Função assíncrona para alterar o idioma e salvar no AsyncStorage
  async function setLanguage(lang: string) {
    try {
      await AsyncStorage.setItem(LANGUAGE_STORAGE_KEY, lang); // Salva o novo idioma no armazenamento
      setLanguageState(lang); // Atualiza o estado com o novo idioma
    } catch (error) {
      console.error('Error saving language:', error); // Exibe erro se houver problema ao salvar
    }
  }

  // Função que retorna a tradução correspondente para uma chave de tradução
  function translate(key: string, section?: keyof Translations): string {
    const keys = key.split('.'); // Divide a chave em partes se usar notação de ponto (ex: "perfil.nome")
    let translation: any = translations[language as keyof typeof translations]; // Obtém o objeto de tradução para o idioma atual
    
    // Se houver uma seção definida (como 'perfil'), acessa essa parte específica das traduções
    if (section) {
      translation = translation[section];
    }
    
    // Percorre as chaves da tradução para chegar no valor final
    for (const k of keys) {
      if (translation[k] === undefined) {
        console.warn(`Translation not found for key: ${key}`); // Se não encontrar, mostra um aviso e retorna a própria chave como fallback
        return key;
      }
      translation = translation[k]; // Navega para o próximo nível da tradução
    }
    
    return translation; // Retorna a tradução final
  }

  // Retorna o provider do contexto, fornecendo os valores e funções para os componentes filhos
  return (
    <LanguageContext.Provider 
      value={{ 
        language, // Idioma atual
        setLanguage, // Função para mudar o idioma
        translate, // Função de tradução
        availableLanguages // Lista de idiomas disponíveis
      }}
    >
      {children} {/* Renderiza os componentes filhos */}
    </LanguageContext.Provider>
  );
}

// Hook personalizado para facilitar o uso do contexto em outros componentes
export function useLanguage(): LanguageContextData {
  const context = useContext(LanguageContext); // Usa o contexto criado

  // Se o contexto não for encontrado (caso o componente não esteja dentro do provider), lança um erro
  if (!context) {
    throw new Error('useLanguage deve ser usado dentro de um LanguageProvider');
  }

  return context; // Retorna o contexto
}
```

- **Tipo e Interface**:

    * `Translations` define o tipo das traduções, assumindo que a estrutura do arquivo de tradução em Português é igual para todos os idiomas.
    * `LanguageContextData` define a interface para o contexto de idioma, contendo o idioma atual, a função para mudar de idioma (setLanguage), a função de tradução (t) e a lista de idiomas disponíveis.


- LanguageProvider:

    * Gerencia o idioma atual usando `useState`, com valor inicial pt (Português).
    * Ao montar, ele carrega o idioma previamente salvo do `AsyncStorage` usando `useEffect` e a função `loadSavedLanguage`.
    * `setLanguage` é uma função assíncrona que permite trocar o idioma e salva a nova configuração no armazenamento local.


- Função `translate` para Traduções:

    - A função `translate` recebe uma chave (como `"perfil.nome"`) e retorna a tradução correspondente.
    - Ela navega pelos objetos de tradução até encontrar o valor correspondente à chave, tratando traduções que usam uma notação de ponto.


- **Uso do Provider**:

    - O `LanguageContext.Provider` fornece o idioma atual, a função de alteração de idioma, a função de tradução e a lista de idiomas para qualquer componente filho dentro do provider.

### Página Settings

```typescript
// app/settings.tsx
import { View, Text, TouchableOpacity } from 'react-native';
import { styled } from 'nativewind';
import { useLanguage } from '../../context/languageContext';

const StyledView = styled(View);
const StyledText = styled(Text);
const StyledTouchableOpacity = styled(TouchableOpacity);

export default function Settings() {
  const { translate, language, setLanguage, availableLanguages } = useLanguage();

  return (
    <StyledView className="flex-1 p-5 bg-white">
      <StyledText className="text-2xl font-bold mb-6">
        {translate('settings.title')}
      </StyledText>

      <StyledText className="text-lg mb-4">
        {translate('settings.language')}
      </StyledText>

      {availableLanguages.map((lang) => (
        <StyledTouchableOpacity
          key={lang.code}
          className={`p-4 mb-2 rounded-lg border ${
            language === lang.code ? 'bg-primary' : 'bg-white'
          }`}
          onPress={() => setLanguage(lang.code)}
        >
          <StyledText 
            className={`text-lg ${
              language === lang.code ? 'text-white' : 'text-gray-900'
            }`}
          >
            {lang.name}
          </StyledText>
        </StyledTouchableOpacity>
      ))}
    </StyledView>
  );
}
```

Este código exibe uma tela de configurações de idioma, permitindo que o usuário alterne entre diferentes idiomas suportados. A interface se adapta de forma dinâmica com base no idioma atual, usando estilos condicionais para destacar o idioma selecionado.



