遇到个问题,在 App 中有一个全局退出登录弹框(应用收到 Token 过期通知,需要强制用户重新登录),当用户在首页时能正常显示;但当用户跳转到子页面时,触发退出登录逻辑后,弹框“不见了”——实际上弹框已渲染,但被子页面遮挡,用户看不到也无法操作,回到主页才能看到。

退出登录弹框(NormalContentDialog)被渲染在 首页(HomePage) 的 render 方法中。

render() {
    return (
        <View>
            {/* 页面内容 */}
            <NormalContentDialog isShow={this.state.showLogOutDialog} ... />
        </View>
    );
}

根因分析:

在 React Navigation 的 Stack 导航中,首页(HomePage)位于导航栈底层。跳转到子页面时,子页面被推入栈顶,覆盖在首页之上。虽然首页并未被卸载,弹框也依然存在于首页的视图树中,但因为子页面在视图层级上更高,弹框被子页面完全遮挡。由于弹框使用绝对定位覆盖全屏,但受限于父容器(首页)的布局范围,无法突破导航栈的层级限制。这是组件层级与导航栈冲突了。

使用 Modal 方案,把弹框用 RN 的 Modal 组件包裹,利用 Modal 的独立窗口特性覆盖所有页面。但是,Android 16 上 Modal 对系统返回键有默认行为——点击返回键会强制关闭弹框,且难以完全拦截。业务要求“点击返回键弹框不消失”。

最终方案:纯 View + 根组件提升

即,将退出登录弹框从首页(HomePage)提升到应用根组件(App.js)。在 App.js 中,将弹框渲染在 <NavigationContainer> 之后,利用 position: 'absolute' 覆盖全屏,并用极高的 zIndex 确保位于所有页面之上。不使用 Modal,而是用普通的 View 作为容器,配合 BackHandler 完全控制返回键行为,彻底规避 Android 16 的兼容性隐患。各页面的本地弹框逻辑统一迁移到 App 层,避免重复渲染。

App.js 中的全局弹框容器:

// render 方法中
<NavigationContainer ...>
    {/* 所有页面 */}
</NavigationContainer>

{/* 全局退出弹框*/}
{this.state.showGlobalLogoutDialog && (
    <View
        style={{
            position: 'absolute',
            top: 0,
            left: 0,
            right: 0,
            bottom: 0,
            backgroundColor: 'rgba(0,0,0,0.5)',
            zIndex: 999999,
            elevation: 999999, 
            justifyContent: 'flex-end',
        }}
    >
        <NormalContentDialog
            isShow={true}
            // 其他属性。。。
        />
    </View>
)}

返回键拦截:

// App.js 的 componentDidMount方法中:
this.tokenExpireListener = DeviceEventEmitter.addListener('tokenExpire', () => {
    this.setState({ showGlobalLogoutDialog: true });
    
    // 弹框显示时拦截返回键
    if (!this.backHandler) {
        this.backHandler = BackHandler.addEventListener('hardwareBackPress', () => {
            if (this.state.showGlobalLogoutDialog) {
                return true; // 消费事件,弹框不关闭
            }
            return false;
        });
    }
});

测试ok.

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐