Tratamento de Erros e Exceções
Neste projeto, o tratamento de erros é fundamentado no uso de Classes de Erro Customizadas e na Propagação de Exceções entre as camadas até chegar à Camada de Tela (Screen), que é a única responsável pela interação visual com o usuário.
1. Classes de Erro Customizadas
Todo erro de negócio ou de fluxo deve ser criado como uma classe que estende a classe nativa Error. Isso nos permite usar o operador instanceof para identificar o erro específico e tomar decisões baseadas nele.
Por que usar classes?
- Tipagem Forte: O TypeScript reconhece o tipo do erro.
- Contexto: Podemos adicionar propriedades extras à classe de erro (ex:
statusCode,metadata). - Semântica: O nome da classe explica o que aconteceu (ex:
UnauthorizedError).
2. Exemplo Real: Conflito de Perfis (409)
Imagine um cenário de login onde o usuário possui múltiplos perfis (ex: Admin e Doutor). A API retorna um erro 409 Conflict. A aplicação deve capturar isso, entender que se trata de múltiplos perfis e direcionar o usuário para uma tela de escolha.
Passo 1: Definição da Classe (Camada Application/Domain)
// src/actions/auth/errors/multiples-profile-error.ts
export class MultiplesProfileError extends Error {
public readonly profiles: string[];
constructor(profiles: string[]) {
super("Usuário possui múltiplos perfis vinculados.");
this.name = "MultiplesProfileError";
this.profiles = profiles;
// Necessário para que o instanceof funcione corretamente após a transpilação
Object.setPrototypeOf(this, MultiplesProfileError.prototype);
}
}Passo 2: Captura e Transformação (Camada Application/Action)
A Action captura o erro bruto da API e o transforma em nossa classe de erro de domínio (ou apenas relança o erro).
// src/actions/auth/login.action.ts
export async function loginAction(credentials: Credentials) {
try {
const response = await api.post("/login", credentials);
return response.data;
} catch (error) {
if (error.response?.status === 409) {
// Transforma o erro 409 da API em um erro de domínio conhecido
throw new MultiplesProfileError(error.response.data.profiles);
}
// Outros erros genéricos são apenas relançados para as camadas superiores
throw error;
}
}Passo 3: Tratamento Visual (Camada Screen)
O Hook da tela captura o erro lançado pela Action (que atravessa a camada de Feature) e a Screen decide o que fazer.
// src/screens/Login/login-screen.hooks.ts
export function useLoginScreen() {
const navigate = useNavigate();
const handleLogin = async (data: Credentials) => {
try {
await loginAction(data);
navigate("Home");
} catch (error) {
if (error instanceof MultiplesProfileError) {
// Direciona para a tela de escolha de perfil com os dados do erro
navigate("SelectProfile", { profiles: error.profiles });
return;
}
// Tratamento visual (Toast) é EXCLUSIVO da Screen
Toast.show({ title: "Erro", message: error.message, type: "error" });
}
};
return { handleLogin };
}3. Fluxo de Erro (Propagation)
A regra de ouro é: O erro deve subir as camadas.
- Action (Application): Captura o erro da API (Axios/Fetch), loga se necessário e faz o
throwde uma classe customizada ou do erro bruto. Nunca exibe feedback visual. - Rule/Feature: Pode capturar o erro para adicionar lógica de negócio ou apenas deixar passar.
- Screen Hook: Captura o erro final via
try/catchou pelo estado de erro doTanStack Query. - Screen: Reage visualmente (Navegação, Toast, Modal ou Feedback inline).
4. Quando usar e Quando não usar
Usar quando:
- O erro exige uma ação específica do usuário (ex: trocar senha, escolher perfil).
- O erro precisa ser diferenciado de outros erros genéricos de rede.
- Você precisa passar dados extras dentro do erro.
Não usar quando:
- For um erro genérico de "Algo deu errado" que apenas mostrará um Toast padrão.
- For um erro de sintaxe ou erro de programação (esses devem ser corrigidos, não tratados como fluxo).
5. Sintaxe Obrigatória para Componentes
Lembre-se que, ao exibir o erro em componentes de feedback, sempre use a sintaxe de function:
function ErrorFeedback({ message }: { message: string }) {
return (
<View style={styles.container}>
<Icon name="error" />
<Text>{message}</Text>
</View>
);
}