You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Expo Router时出现找不到导航上下文的问题求助

问题描述

基于Expo 52搭建的项目,使用Expo Router、自定义React Query客户端及路由守卫。原本运行正常,在开发引导页流程后,随机出现“找不到导航上下文”的错误,且该错误仅在从step1页面跳转至step2页面时触发。


项目文件夹结构

your-project/
├── app/
│   ├── (auth)/
│   │   ├── onboarding/
│   │   │   ├── step1.tsx
│   │   │   ├── step2.tsx
│   │   ├── _layout.tsx
│   ├── _layout.tsx
├── components/
│   ├── RouteGuard.tsx
│   ├── CustomQueryClientProvider.tsx
├── package.json

错误日志

Error: Could not find a navigation context. Is your component inside a screen in a navigator?
    at useNavigation (useNavigation.tsx:16:11)
    at Step1 (step1.tsx:XX:XX)
    ...

相关代码

1. 路由守卫(RouteGuard.tsx)

import React from 'react';
import { useRouter, useSegments } from 'expo-router';
import { useQueryClient } from '@tanstack/react-query';

export default function RouteGuard({ children }: { children: React.ReactNode }) {
  const router = useRouter();
  const segments = useSegments();
  const queryClient = useQueryClient();

  React.useEffect(() => {
    const isOnboardingCompleted = queryClient.getQueryData(['onboarding-status']);
    if (!isOnboardingCompleted && segments[0] !== '(auth)/onboarding/step1') {
      router.replace('/(auth)/onboarding/step1');
    }
  }, [segments, queryClient, router]);

  return <>{children}</>;
}

2. 自定义React Query Provider(CustomQueryClientProvider.tsx)

import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import React from 'react';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 60000,
    },
  },
});

export default function CustomQueryClientProvider({ children }: { children: React.ReactNode }) {
  return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
}

3. App路由布局(app/_layout.tsx)

import { Stack } from 'expo-router';
import CustomQueryClientProvider from '../components/CustomQueryClientProvider';
import RouteGuard from '../components/RouteGuard';

export default function RootLayout() {
  return (
    <CustomQueryClientProvider>
      <RouteGuard>
        <Stack>
          <Stack.Screen name="(auth)" options={{ headerShown: false }} />
        </Stack>
      </RouteGuard>
    </CustomQueryClientProvider>
  );
}

4. Step1页面跳转逻辑(step1.tsx)

import { useRouter } from 'expo-router';
import { Button } from 'react-native';

export default function Step1() {
  const router = useRouter();

  const handleNext = () => {
    router.push('/(auth)/onboarding/step2');
  };

  return <Button title="下一步" onPress={handleNext} />;
}

解决方案

1. 调整路由守卫的嵌套顺序

Expo Router的导航上下文由Stack/Tabs等组件提供,当前布局中RouteGuard包裹了Stack,导致守卫内部无法获取导航上下文。修改布局,将路由守卫作为屏幕的子组件:

修改app/_layout.tsx:

import { Stack } from 'expo-router';
import CustomQueryClientProvider from '../components/CustomQueryClientProvider';
import RouteGuard from '../components/RouteGuard';

export default function RootLayout() {
  return (
    <CustomQueryClientProvider>
      <Stack>
        <Stack.Screen 
          name="(auth)" 
          options={{ headerShown: false }}
          children={({ children }) => <RouteGuard>{children}</RouteGuard>} 
        />
      </Stack>
    </CustomQueryClientProvider>
  );
}

2. 确保状态更新完成后再跳转

随机错误可能源于React Query状态更新与路由跳转的时机冲突,在跳转前确保状态更新完成:

修改step1.tsx:

import { useRouter } from 'expo-router';
import { Button } from 'react-native';
import { useQueryClient } from '@tanstack/react-query';

export default function Step1() {
  const router = useRouter();
  const queryClient = useQueryClient();

  const handleNext = async () => {
    await queryClient.setQueryData(['onboarding-status'], true);
    router.push('/(auth)/onboarding/step2');
  };

  return <Button title="下一步" onPress={handleNext} />;
}

3. 使用useFocusEffect替代普通useEffect

在路由守卫中,使用Expo Router提供的useFocusEffect监听路由变化,避免在上下文未就绪时触发逻辑:

修改RouteGuard.tsx:

import React from 'react';
import { useRouter, useSegments, useFocusEffect } from 'expo-router';
import { useQueryClient } from '@tanstack/react-query';

export default function RouteGuard({ children }: { children: React.ReactNode }) {
  const router = useRouter();
  const segments = useSegments();
  const queryClient = useQueryClient();

  useFocusEffect(
    React.useCallback(() => {
      const isOnboardingCompleted = queryClient.getQueryData(['onboarding-status']);
      if (!isOnboardingCompleted && segments[0] !== '(auth)/onboarding/step1') {
        router.replace('/(auth)/onboarding/step1');
      }
    }, [segments, queryClient, router])
  );

  return <>{children}</>;
}

内容的提问来源于stack exchange,提问作者Ash Gharibyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 15:35:54