如何在使用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
相关产品推荐
相关产品推荐

