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

如何在React Native Navigation中按条件禁用所有导航操作

问题根因

你遇到的两个问题都是React Navigation的实现细节没处理到位导致的:

  • 状态判断失效:你给useEffect传了空依赖数组[],注册的beforeRemove监听器会形成闭包,永远拿到组件首次挂载时hasRecordingStarted的初始值false,后续状态更新后监听器里的判断逻辑用的还是旧值,拦截逻辑自然不会生效。
  • 拦不住Tab等导航动作:一方面是上面说的闭包导致拦截逻辑根本没执行,另一方面是默认配置下的底部Tab导航切换时,不会卸载非活跃Tab的页面,beforeRemove只有在页面被移出导航栈时才会触发,所以切Tab的动作根本进不到你之前写的拦截逻辑里。
正确实现代码

先处理通用导航拦截(覆盖返回按钮、代码调用navigate/reset跳转、页面销毁类跳转),注意要把依赖项正确传入,同时做好监听器清理,用户确认离开后要主动派发被拦截的导航动作:

import { useState, useEffect, useLayoutEffect } from 'react';
import { useNavigation } from '@react-navigation/native';
import { Alert } from 'react-native';

// 页面组件内逻辑
const [hasRecordingStarted, setHasRecordingStarted] = useState(false);
const navigation = useNavigation();

// 拦截所有会触发页面卸载的导航动作
useEffect(() => {
  const handleBeforeRemove = (e) => {
    if (!hasRecordingStarted) return;

    // 阻止默认跳转行为
    e.preventDefault();

    Alert.alert(
      '录制中无法离开页面',
      '请先停止录制,保存或丢弃当前录制路线后再操作',
      [
        {
          text: '留在当前页',
          style: 'cancel'
        },
        {
          text: '放弃录制离开',
          style: 'destructive',
          onPress: () => {
            setHasRecordingStarted(false);
            // 执行原本被拦截的跳转动作,兼容所有跳转目标
            navigation.dispatch(e.data.action);
          }
        }
      ]
    );
  };

  const unsubscribe = navigation.addListener('beforeRemove', handleBeforeRemove);
  // 依赖变化、组件卸载时清理旧监听器,避免重复触发
  return unsubscribe;
}, [hasRecordingStarted, navigation]);

如果你的页面在Tab导航下,需要额外拦截Tab切换动作(默认切Tab不会卸载页面,因此不会触发beforeRemove):

// 拦截Tab点击切换动作
useLayoutEffect(() => {
  const handleTabPress = (e) => {
    if (!hasRecordingStarted) return;

    e.preventDefault();
    // 可将弹窗逻辑抽为公共函数复用,避免重复代码
    Alert.alert(
      '录制中无法离开页面',
      '请先停止录制,保存或丢弃当前录制路线后再操作'
    );
  };

  const unsubscribeTab = navigation.addListener('tabPress', handleTabPress);
  return unsubscribeTab;
}, [hasRecordingStarted, navigation]);
补充说明
  • 如果项目里用了抽屉导航,需要用同样逻辑拦截drawerItemPress事件,即可覆盖所有导航离开场景。
  • 不要在拦截逻辑里硬编码跳转目标,直接调用事件对象自带的e.data.action执行原跳转即可,适配所有跳转类型。
  • 所有注册的监听器必须返回清理函数,避免重复注册导致弹窗多次触发、内存泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 17:48:20