Expo Router 在 useEffect 中跳转时提示 Root Layout 未挂载怎么办?
结论
先让 Root Layout 渲染 <Slot /> 或 <Stack /> 等导航组件,再执行跳转。把 router.replace() 放进 useEffect 并不能保证导航已经就绪。
登录拦截可以这样处理:根布局负责提供鉴权状态并渲染导航,未登录时的重定向放在受保护路由组的嵌套布局中。Expo 的鉴权重定向指南也采用了这种结构。
为什么 useEffect 中仍然报错?
日志显示,app/_layout.tsx 在 isAuth 为假时调用了:
route.replace("/(auth)/sign-in");
此时路由系统报告 Root Layout 尚未挂载。useEffect 执行时,对应组件已经完成一次提交;但如果那次渲染没有输出导航组件,仍然没有可用于跳转的导航结构。
例如,下面的写法在鉴权加载期间不渲染 <Slot />,但仍可能尝试跳转:
useEffect(() => {
if (!isAuth) {
router.replace('/(auth)/sign-in');
}
}, [isAuth]);
if (isLoading) {
return null;
}
return <Slot />;
这是一种可能的触发方式。由于没有完整的布局代码,无法断定项目中是否存在这个分支。还需要检查包裹 <Slot /> 的 Provider 是否在初始化时返回了 null 或加载页面。
如何调整布局和鉴权逻辑?
以下是建议的目录结构,并非原始项目的实际目录:
app/
├── _layout.tsx
├── (auth)/
│ └── sign-in.tsx
└── (app)/
├── _layout.tsx
└── index.tsx
登录页放在 (auth) 组,需要登录的页面放在 (app) 组。
示例假设项目中有 auth.tsx,提供 AuthProvider 和 useAuth(),后者返回 isAuth 与 isLoading。这些是示例中的自定义接口,需要替换为项目已有的实现;isLoading 表示登录状态是否仍在恢复或验证中。
1. 根布局渲染导航入口
app/_layout.tsx:
import { Slot } from 'expo-router';
import { AuthProvider } from '../auth';
export default function RootLayout() {
return (
<AuthProvider>
<Slot />
</AuthProvider>
);
}
AuthProvider 应通过 Context 暴露加载状态,同时继续渲染 children。删除根布局中原来负责登录拦截的 useEffect,并检查 Provider 内是否还有启动时的导航调用。
2. 在受保护路由组中处理加载和重定向
app/(app)/_layout.tsx:
import { Redirect, Stack } from 'expo-router';
import { Text } from 'react-native';
import { useAuth } from '../../auth';
export default function ProtectedLayout() {
const { isAuth, isLoading } = useAuth();
if (isLoading) {
return <Text>正在检查登录状态…</Text>;
}
if (!isAuth) {
return <Redirect href="/(auth)/sign-in" />;
}
return <Stack />;
}
这里先等待鉴权完成,再根据登录状态决定重定向还是渲染页面。登录页位于受保护组之外,未登录用户才能正常访问。这个结构对应官方指南中的嵌套布局鉴权示例。
还需要检查什么?
- 确认
/(auth)/sign-in对应的页面确实存在。当前材料没有实际目录结构,无法核对路径。 - 核对日志中的
route如何定义。如果它来自const route = useRouter(),变量名可以保留;如果来自其他 API,则需要检查调用对象。 - 不要把尚未读取到登录状态直接当成未登录。异步恢复会话时,应单独维护加载状态。
- 不要用固定的
setTimeout延迟掩盖挂载问题;等待一定时间不能保证导航已经就绪。
如果修改后仍然报错,需要结合完整的 app/_layout.tsx、Provider、目录结构及 Expo SDK、expo-router 版本继续定位。SDK 53 引入了 Protected routes;使用支持该功能的版本时,也可以参考官方鉴权指南,通过 Stack.Protected 管理访问条件。
备注:内容仅供参考。
