如何基于wix/react-native-navigation实现全局HOC管控网络状态页面切换
我之前在做RN项目时也碰到过类似需求,在wix/react-native-navigation这种组件独立注册的架构下,要给所有页面统一加全局逻辑,这里分享几个实操性强的方案,你可以根据项目情况选:
方案一:根组件全局控制(最推荐)
这种方式不用给每个页面单独加HOC,而是在导航根节点层面根据网络状态切换整个导航结构,逻辑集中,也不会出现重复跳转的问题。
步骤1:封装网络状态监听工具
先写个简单的工具类管理全局网络状态,方便后续组件获取和监听:
// utils/network.js import NetInfo from '@react-native-community/netinfo'; let isConnected = true; let listeners = []; // 初始化网络监听 NetInfo.addEventListener(state => { const newConnected = state.isConnected && state.isInternetReachable; if (newConnected !== isConnected) { isConnected = newConnected; listeners.forEach(listener => listener(isConnected)); } }); // 订阅网络状态变化 export function subscribeNetworkChange(listener) { listeners.push(listener); return isConnected; // 立即返回当前状态 } // 取消订阅 export function unsubscribeNetworkChange(listener) { listeners = listeners.filter(l => l !== listener); } // 获取当前网络状态 export function getNetworkStatus() { return isConnected; }
步骤2:创建根控制组件AppRoot
这个组件作为应用入口,负责根据网络状态切换根导航布局:
// components/AppRoot.js import React, { useEffect, useCallback } from 'react'; import { Navigation } from 'react-native-navigation'; import { subscribeNetworkChange, unsubscribeNetworkChange } from '../utils/network'; // 提前注册无网络页面 Navigation.registerComponent('NoNetworkScreen', () => require('./NoNetworkScreen').default); // 正常网络下的标签页布局(替换成你自己的标签页配置) const getTabLayout = () => ({ root: { bottomTabs: { children: [ { stack: { children: [{ component: { name: 'Home' } }], options: { bottomTab: { text: '首页' } } } }, { stack: { children: [{ component: { name: 'Profile' } }], options: { bottomTab: { text: '我的' } } } } // 其他标签页... ] } } }); // 无网络时的单页面布局 const getNoNetworkLayout = () => ({ root: { stack: { children: [{ component: { name: 'NoNetworkScreen' } }] } } }); export default function AppRoot() { useEffect(() => { // 初始化时设置初始布局 const initialConnected = subscribeNetworkChange(handleNetworkChange); handleNetworkChange(initialConnected); // 组件卸载时取消监听 return () => unsubscribeNetworkChange(handleNetworkChange); }, []); const handleNetworkChange = useCallback((isConnected) => { // 根据网络状态切换根导航 if (isConnected) { Navigation.setRoot(getTabLayout()); } else { Navigation.setRoot(getNoNetworkLayout()); } }, []); // RN Navigation根组件无需渲染内容,靠setRoot控制布局 return null; }
步骤3:启动应用时注册根组件
把原来直接启动标签页的逻辑改成启动AppRoot:
// index.js import { Navigation } from 'react-native-navigation'; import AppRoot from './components/AppRoot'; // 提前注册所有业务页面组件 Navigation.registerComponent('Home', () => require('./components/Home').default); Navigation.registerComponent('Profile', () => require('./components/Profile').default); // 启动根控制组件 Navigation.registerComponent('AppRoot', () => AppRoot); Navigation.setRoot({ root: { component: { name: 'AppRoot' } } });
这个方案的优势是完全不修改现有页面代码,逻辑全集中在根组件,网络状态变化时直接切换整个导航根,用户体验很流畅。
方案二:给所有注册组件统一加HOC
如果你的项目还需要给每个页面加其他全局逻辑(比如登录态校验),可以用HOC统一包裹所有注册组件:
步骤1:编写网络检查HOC
// hoc/withNetworkCheck.js import React, { useEffect, useRef } from 'react'; import { Navigation } from 'react-native-navigation'; import { getNetworkStatus, subscribeNetworkChange, unsubscribeNetworkChange } from '../utils/network'; export default function withNetworkCheck(WrappedComponent) { return function NetworkCheckHOC(props) { const isCurrentPageActive = useRef(true); useEffect(() => { const handleNetworkChange = (isConnected) => { // 仅当前页面活跃时处理跳转,避免重复跳转 if (isCurrentPageActive.current && !isConnected) { Navigation.push(props.componentId, { component: { name: 'NoNetworkScreen' } }); } }; const initialConnected = subscribeNetworkChange(handleNetworkChange); if (!initialConnected) handleNetworkChange(false); return () => { unsubscribeNetworkChange(handleNetworkChange); isCurrentPageActive.current = false; }; }, [props.componentId]); return <WrappedComponent {...props} />; }; }
步骤2:注册组件时统一使用HOC
把所有组件注册代码改成:
Navigation.registerComponent('Home', () => withNetworkCheck(require('./components/Home').default)); Navigation.registerComponent('Profile', () => withNetworkCheck(require('./components/Profile').default)); // 所有业务页面都用这个HOC包裹
注意:网络恢复后,需要在NoNetworkScreen里监听状态,调用Navigation.pop()或Navigation.popToRoot()回到原页面,避免用户手动返回时出现异常。
方案三:利用RN Navigation全局事件监听
如果需求简单,也可以通过监听全局页面显示事件来做拦截:
// 在App启动时添加全局监听 Navigation.events().registerComponentDidAppearListener(({ componentId, componentName }) => { // 排除无网络页面本身,避免死循环 if (componentName === 'NoNetworkScreen') return; const isConnected = getNetworkStatus(); if (!isConnected) { Navigation.push(componentId, { component: { name: 'NoNetworkScreen' } }); } });
这个方式最简洁,但缺点是无法处理页面后台时的网络变化,逻辑也比较分散,适合简单场景。
综合来看,方案一最适配你的需求,既不用改动现有页面,又能优雅控制全局导航切换。
内容的提问来源于stack exchange,提问作者Андрей Гузюк
相关产品推荐
相关产品推荐

