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

如何在使用Expo Router的React Native项目中切换页面时显示加载指示器?

如何在使用Expo Router的React Native项目中切换页面时显示加载指示器?

嘿,这个需求我刚好踩过坑!在Expo Router里实现页面跳转时的加载状态其实没那么复杂,核心就是把全局加载状态和路由导航事件结合起来,下面给你一步步拆解实现方法:

第一步:创建全局加载状态的Context

我们需要一个全局状态来统一管理加载状态,这样所有页面和组件都能轻松访问和修改它。新建一个Context文件:

// app/context/LoadingContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';

type LoadingContextType = {
  isLoading: boolean;
  setIsLoading: (value: boolean) => void;
};

const LoadingContext = createContext<LoadingContextType | undefined>(undefined);

export function LoadingProvider({ children }: { children: ReactNode }) {
  const [isLoading, setIsLoading] = useState(false);

  return (
    <LoadingContext.Provider value={{ isLoading, setIsLoading }}>
      {children}
    </LoadingContext.Provider>
  );
}

export function useLoading() {
  const context = useContext(LoadingContext);
  if (!context) {
    throw new Error('useLoading必须在LoadingProvider内部使用哦');
  }
  return context;
}

第二步:在根布局中配置全局加载指示器和路由监听

Expo Router的根布局是所有页面的父容器,非常适合放置全局加载指示器,同时在这里监听路由的导航事件,自动控制加载状态的切换:

// app/_layout.tsx
import { Stack } from 'expo-router';
import { LoadingProvider } from './context/LoadingContext';
import { ActivityIndicator, View, StyleSheet } from 'react-native';
import { useLoading } from './context/LoadingContext';
import { useEffect } from 'react';
import { useRouter } from 'expo-router';

export default function RootLayout() {
  const { isLoading, setIsLoading } = useLoading();
  const router = useRouter();

  useEffect(() => {
    // 导航开始时显示加载指示器
    const handleBeforeNavigate = () => {
      setIsLoading(true);
    };

    // 导航完成后隐藏加载指示器,加个小延迟避免闪烁
    const handleAfterNavigate = () => {
      setTimeout(() => setIsLoading(false), 300);
    };

    // 注册路由事件监听
    const unsubscribeBefore = router.addListener('beforeNavigate', handleBeforeNavigate);
    const unsubscribeAfter = router.addListener('afterNavigate', handleAfterNavigate);

    // 组件卸载时取消监听,防止内存泄漏
    return () => {
      unsubscribeBefore();
      unsubscribeAfter();
    };
  }, [router, setIsLoading]);

  return (
    <LoadingProvider>
      {/* Expo Router的Stack导航配置 */}
      <Stack>
        <Stack.Screen name="login" options={{ title: '登录页' }} />
        <Stack.Screen name="signup" options={{ title: '注册页' }} />
      </Stack>

      {/* 全局加载指示器,绝对定位覆盖整个屏幕 */}
      {isLoading && (
        <View style={styles.loadingOverlay}>
          <ActivityIndicator size="large" color="#2563eb" />
        </View>
      )}
    </LoadingProvider>
  );
}

const styles = StyleSheet.create({
  loadingOverlay: {
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
    bottom: 0,
    backgroundColor: 'rgba(0, 0, 0, 0.1)',
    justifyContent: 'center',
    alignItems: 'center',
    zIndex: 9999, // 确保在所有组件上方
  },
});

第三步:页面内的自定义加载逻辑(可选)

如果你的页面有异步数据加载(比如请求接口),可以在页面内部结合全局加载状态,确保数据加载完成后再隐藏指示器:

// app/login.tsx
import { View, Text, Button, StyleSheet } from 'react-native';
import { useRouter } from 'expo-router';
import { useEffect, useState } from 'react';
import { useLoading } from './context/LoadingContext';

export default function LoginPage() {
  const router = useRouter();
  const { setIsLoading } = useLoading();
  const [loginData, setLoginData] = useState(null);

  useEffect(() => {
    // 模拟页面数据加载
    const fetchLoginData = async () => {
      setIsLoading(true);
      try {
        // 替换成你的实际接口请求
        const response = await fetch('https://api.example.com/login-config');
        const data = await response.json();
        setLoginData(data);
      } catch (error) {
        console.error('数据加载失败:', error);
      } finally {
        setIsLoading(false);
      }
    };

    fetchLoginData();
  }, [setIsLoading]);

  return (
    <View style={styles.pageContainer}>
      <Text style={styles.pageTitle}>登录页</Text>
      {loginData && <Text style={styles.dataText}>加载的配置:{JSON.stringify(loginData)}</Text>}
      <Button title="跳转到注册" onPress={() => router.push('/signup')} />
    </View>
  );
}

const styles = StyleSheet.create({
  pageContainer: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  pageTitle: {
    fontSize: 20,
    marginBottom: 20,
  },
  dataText: {
    marginVertical: 15,
    color: '#666',
  },
});

一些优化小技巧

  • 给加载指示器添加半透明背景,提升视觉体验,避免用户觉得界面卡住
  • 最小显示时间:比如设置至少显示200ms,防止快速导航时指示器一闪而过
  • 错误处理:如果导航失败或者数据请求出错,记得也要把加载状态设为false,避免指示器一直显示

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:57:57