React Native结合React Navigation5时useEffect同屏/返回/切后台不触发问题
要覆盖你提到的所有场景,需要同时处理三类触发时机:页面聚焦、应用前后台切换、停留当前页时的时间到达触发,具体实现如下:
1. 实现思路
- 使用React Navigation提供的
useFocusEffect处理页面聚焦时的校验,替代仅依赖静态变量触发的useEffect - 使用React Native内置的
AppState监听应用从后台切回前台的事件,每次切回主动触发校验 - 为当前选中的配送时间计算倒计时,到达过期时间点时自动触发校验,无需跳转页面即可执行逻辑
2. 完整修改代码
import * as React from 'react'; import {Text,View, Alert, Button, AppState} from 'react-native'; import { NavigationContainer, useFocusEffect } from '@react-navigation/native'; import { createStackNavigator } from '@react-navigation/stack'; import { useNavigation } from '@react-navigation/native'; const Stack = createStackNavigator(); const deliveryDate = "2021-06-21"; const deliveryTime = "10:00-10:03"; const isTimeWindowExpired = (date: string, time: string) => { const [startTime] = time.split('-'); const newDate = new Date(`${date}T${startTime}`); return newDate.getTime() < new Date().getTime(); }; function Home() { const navigation = useNavigation() return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>Home</Text> <Button title="Go to Details" onPress={() => navigation.navigate('details')} /> </View> ); } function SecondHome() { const navigation = useNavigation(); const appState = React.useRef(AppState.currentState); const expirationTimer = React.useRef<NodeJS.Timeout | null>(null); // 通用校验逻辑 const checkExpiration = React.useCallback(() => { const isExpired = isTimeWindowExpired(deliveryDate, deliveryTime); if (isExpired) { Alert.alert("您选择的时间已过期!"); // 这里追加清空配送时间的逻辑 } // 计算下一次需要触发校验的时间,设置定时器 if (expirationTimer.current) clearTimeout(expirationTimer.current); const [startTime] = deliveryTime.split('-'); const expireTime = new Date(`${deliveryDate}T${startTime}`).getTime(); const now = Date.now(); if (expireTime > now) { expirationTimer.current = setTimeout(checkExpiration, expireTime - now); } }, [deliveryDate, deliveryTime]); // 页面聚焦时触发校验 useFocusEffect( React.useCallback(() => { checkExpiration(); return () => { if (expirationTimer.current) clearTimeout(expirationTimer.current); }; }, [checkExpiration]) ); // 监听应用前后台切换 React.useEffect(() => { const subscription = AppState.addEventListener('change', nextAppState => { if (appState.current.match(/inactive|background/) && nextAppState === 'active') { // 应用从后台切回前台,触发校验 checkExpiration(); } appState.current = nextAppState; }); return () => { subscription.remove(); if (expirationTimer.current) clearTimeout(expirationTimer.current); }; }, [checkExpiration]); return ( <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}> <Text>当前页面即可自动校验过期时间,切后台返回也会触发校验</Text> <Text>Home</Text> <Button title="Go back Home" onPress={() => navigation.goBack()} /> </View> ); } const App = () => { return ( <NavigationContainer> <Stack.Navigator initialRouteName="home" detachInactiveScreens> <Stack.Screen name="home" component={Home} /> <Stack.Screen name="details" component={SecondHome} /> </Stack.Navigator> </NavigationContainer> ); }; export default App;
逻辑说明
- 所有场景的校验逻辑复用同一个
checkExpiration方法,避免逻辑重复 - 定时器仅会在过期时间点触发一次,不会高频轮询,不会影响应用性能
- 页面失焦、组件卸载时会自动清除定时器,避免内存泄漏
内容的提问来源于stack exchange,提问作者user12494839
相关产品推荐
相关产品推荐

