Guardas de Rota e Inicialização
Última atualização: 14 de julho de 2026
Visão Geral
Duas perguntas diferentes precisam ser respondidas sempre que o app é aberto ou navegado. A primeira é para onde o app deve levar o usuário quando ele abre o aplicativo do zero, e a segunda é o que fazer se o usuário tentar acessar uma área protegida sem uma sessão válida. A primeira pergunta é resolvida em app/index.tsx, e a segunda é resolvida pelo TabsAuthGuard.
Arquivos-chave
| Arquivo | Responsabilidade |
|---|---|
app/index.tsx | Gate de entrada do app, decide a primeira rota antes de qualquer tela aparecer |
features/auth/tabs-auth-guard.tsx | Guarda que envolve as tabs e redireciona para o login se a sessão não for válida |
storage/navigation/navigation.storage.ts | resolveStartupRoute, a lógica que decide qual rota restaurar ou usar como fallback |
O gate de entrada
app/index.tsx é a primeira tela que roda quando o app abre. Ela não renderiza nenhuma interface própria além de um indicador de carregamento, porque sua única responsabilidade é decidir para onde navegar antes de qualquer outra tela montar:
// app/index.tsx
export default function Index() {
const router = useRouter();
useEffect(() => {
let cancelled = false;
(async () => {
const token = await AuthService.getValidAccessToken();
if (cancelled) return;
router.replace(
resolveStartupRoute({ isAuthenticated: Boolean(token) }) as any,
);
})();
return () => {
cancelled = true;
};
}, [router]);
return (
<View style={{ flex: 1, alignItems: "center", justifyContent: "center" }}>
<ActivityIndicator size="large" color={colors.primary} />
</View>
);
}
Chamar AuthService.getValidAccessToken() aqui já cobre o caso de o app abrir com um token expirado mas ainda renovável, porque essa função tenta o refresh internamente antes de responder, como descrito em Refresh de Token. Se o refresh funcionar, o app inicia autenticado sem que o usuário perceba que o token estava vencido.
Decidindo a rota inicial
resolveStartupRoute é a função usada tanto por esse gate de entrada quanto pela tela de login depois de um login concluído, e ela decide entre restaurar a última rota visitada ou cair num fallback:
// storage/navigation/navigation.storage.ts
export function resolveStartupRoute(options: {
isAuthenticated: boolean;
authenticatedFallback?: string;
unauthenticatedFallback?: string;
}): string {
const lastVisitedRoute = readLastVisitedRoute();
if (lastVisitedRoute) {
if (
options.isAuthenticated ||
isPublicRestorableRoute(lastVisitedRoute.pathname)
) {
return buildRouteHref(lastVisitedRoute);
}
}
return options.isAuthenticated
? (options.authenticatedFallback ?? AUTHENTICATED_FALLBACK_ROUTE)
: (options.unauthenticatedFallback ?? UNAUTHENTICATED_FALLBACK_ROUTE);
}
A regra de restauração tem uma exceção interessante. Mesmo sem sessão válida, algumas rotas consideradas públicas, como onboarding ou a tela de permissões, ainda podem ser restauradas, listadas em PUBLIC_RESTORABLE_PATHS. Rotas que não fazem sentido restaurar em hipótese alguma, como a própria tela de login, a tela de sincronização ou páginas de erro, ficam de fora através de NON_RESTORABLE_PATHS e NON_RESTORABLE_PREFIXES, e nunca chegam a ser salvas como última rota visitada:
const NON_RESTORABLE_PATHS = new Set([
"/", "/index", "/auth/login.auth", "/sync", "/+not-found", "/_sitemap",
]);
const NON_RESTORABLE_PREFIXES = ["/auth", "/sync"];
const PUBLIC_RESTORABLE_PATHS = new Set(["/onboarding", "/permissions"]);
Se nada puder ser restaurado, o fallback padrão é a home das tabs para quem está autenticado, ou a tela de login para quem não está.
A guarda das tabs
Enquanto app/index.tsx só decide a rota inicial uma vez, no boot, o TabsAuthGuard fica de olho continuamente enquanto o usuário navega dentro da área autenticada, reagindo caso a sessão deixe de ser válida em algum momento, por exemplo depois de um logout automático por sessão expirada:
// tabs-auth-guard.tsx
export function TabsAuthGuard({ children }: { children: React.ReactNode }) {
const router = useRouter();
const { isAuthenticated, isLoading } = useAuth();
useEffect(() => {
if (!isAuthenticated) {
router.replace("/auth/login.auth");
}
}, [isAuthenticated, router]);
return <>{children}</>;
}
Essa guarda depende só do estado reativo de isAuthenticated, exposto pelo AuthProvider, então ela reage automaticamente a qualquer mudança nesse estado, seja um logout manual, seja o logout automático disparado pela validação periódica de sessão descrita em Logout. Vale notar que o valor de isLoading, que existiria para mostrar um indicador de carregamento enquanto uma tentativa de login está em andamento, está lido mas não é usado atualmente para renderizar nada, já que o trecho que faria isso está comentado no arquivo. Isso significa que hoje a guarda não exibe nenhum estado intermediário de carregamento, ela simplesmente redireciona quando detecta ausência de sessão.
Armadilhas conhecidas
Nunca decida a rota inicial em mais de um lugar. Tanto app/index.tsx quanto a tela de login, depois de autenticar, delegam essa decisão para resolveStartupRoute, e um novo ponto de entrada de navegação deveria fazer o mesmo, em vez de reimplementar a lógica de restaurar ou não a última rota. Também não marque uma nova rota como restaurável por padrão sem revisar NON_RESTORABLE_PATHS, NON_RESTORABLE_PREFIXES e PUBLIC_RESTORABLE_PATHS, já que uma rota sensível salva como última rota visitada poderia ser reaberta sem sessão válida se acabar caindo na lista de rotas públicas por engano.