如何在React Navigation v5中阻止导航跳转,适配createStackNavigator场景?
问题一:获取
getStateForAction方法的实现方式 createStackNavigator创建的导航实例本身会暴露默认的router对象,你可以直接扩展该对象重写getStateForAction方法实现导航拦截,修改后的导航器代码如下:
import { createStackNavigator } from '@react-navigation/stack'; import { CommonActions } from '@react-navigation/native'; import { Alert } from 'react-native'; // 此处替换为你自己的全局状态读取/修改逻辑,可使用React Context、Redux、Zustand等实现 import { getGlobalEditState, setGlobalEditState } from './your-global-state'; const StackNavigator = createStackNavigator<TTopLevelParams>(); // 拿到栈导航默认的router逻辑 const defaultRouter = StackNavigator.router; // 重写router的getStateForAction方法 StackNavigator.router = { ...defaultRouter, getStateForAction: (state, action, options) => { const { isEditing, editingRouteName } = getGlobalEditState(); const currentRoute = state.routes[state.index]; // 处于编辑状态且导航动作是离开当前编辑页时,拦截动作 if ( isEditing && action.type === CommonActions.NAVIGATE && currentRoute.name === editingRouteName ) { Alert.alert('提示', '当前存在未保存的内容,确定要离开吗?', [ { text: '取消', style: 'cancel' }, { text: '确定', onPress: () => { setGlobalEditState(false); // 手动执行之前被拦截的导航动作 navigation.dispatch(action); } } ]); // 返回null代表拦截当前导航动作 return null; } // 其他场景走默认导航逻辑 return defaultRouter.getStateForAction(state, action, options); } }; const RootNavigator: React.FC = () => { return ( <StackNavigator.Navigator initialRouteName={StartNav}> <StackNavigator.Screen component={IntroNav} name={IntroNav} /> <StackNavigator.Screen component={StartNav} name={StartNav} /> </StackNavigator.Navigator> ); }; export default RootNavigator;
使用该方案时,只需要在进入编辑页面时更新全局的isEditing和editingRouteName状态,离开/保存编辑内容时重置状态即可。
问题二:其他全局阻止导航跳转的方案
更推荐使用React Navigation 5.x提供的beforeRemove页面事件实现需求,该方案侵入性更低、更易维护,不需要修改全局导航器配置:
- 你可以把拦截逻辑封装为自定义Hook,所有需要拦截离开的编辑页面直接引入调用即可,相当于全局可用的拦截能力
- 该事件会覆盖所有离开页面的场景:包括返回手势、返回按钮点击、调用
navigate/goBack等导航动作
使用示例如下:
// 封装为自定义Hook:usePreventLeaveWhenEditing.js import { useNavigation, useFocusEffect } from '@react-navigation/native'; import { useCallback } from 'react'; import { Alert } from 'react-native'; export const usePreventLeaveWhenEditing = (isEditing) => { const navigation = useNavigation(); useFocusEffect( useCallback(() => { const unsubscribe = navigation.addListener('beforeRemove', (e) => { if (!isEditing) return; // 阻止默认的页面移除动作 e.preventDefault(); Alert.alert( '未保存的更改', '离开将会丢失所有未保存的内容,是否确认离开?', [ { text: '留在页面', style: 'cancel' }, { text: '确认离开', style: 'destructive', onPress: () => navigation.dispatch(e.data.action), }, ] ); }); return unsubscribe; }, [navigation, isEditing]) ); };
在编辑页面中直接使用:
const EditPage = () => { const [isEditing, setIsEditing] = useState(false); // 直接调用Hook传入当前编辑状态即可实现拦截 usePreventLeaveWhenEditing(isEditing); // 页面其他业务逻辑 return <></> }
内容的提问来源于stack exchange,提问作者Lucas Rocha
相关产品推荐
相关产品推荐

