React Native点击通知跳转报_navigator.dispatch未定义错误
通知点击跳转导航问题修复方案
报错现象
实现FCM通知点击跳转指定页面功能时先后触发两个报错:
- 初始运行报错:
undefined is not an object (evaluating '_navigator.dispatch') - 手动绑定导航ref后新增报错:
The action 'Navigation/NAVIGATE' was not handled by any navigator.
问题根因
- 项目使用v5/v6版本的
@react-navigation/native,但现有NavigationService.js是React Navigation v4的旧版API,依赖react-navigation包的NavigationActions和dispatch调用逻辑,和新版本API完全不兼容 - ref绑定逻辑错误:同时给
NavigationContainer、NavStack、NavStack2绑定同一个导航服务ref,会导致ref被反复覆盖,且登录态切换时两个栈导航会交替挂载/卸载,ref极易丢失 - 通知回调触发时机无防护:跳转逻辑执行时导航容器可能还未完成挂载,同时重复注册
onNotificationOpenedApp监听导致逻辑重复触发 - 路由匹配逻辑缺失:要跳转的
MyBooking页面仅在登录后渲染的NavStack2中注册,未登录时渲染的NavStack无对应路由,直接跳转必然触发找不到路由的报错 - 后台消息监听写法错误:
setBackgroundMessageHandler写在组件内部函数中,不符合SDK要求,无法正常生效
修复步骤
1. 替换旧版全局导航服务
废弃原有基于v4写法的NavigationService.js,统一使用RootNavigation.js作为全局导航工具,补充导航就绪判断避免空ref调用:
// RootNavigation.js import React from 'react'; export const navigationRef = React.createRef(); export function isNavigationReady() { return navigationRef.current?.isReady(); } export function navigate(name, params) { if (isNavigationReady()) { navigationRef.current.navigate(name, params); } else { // 导航未就绪时缓存待跳转路由,等容器挂载完成后自动执行 this.pendingNavigation = { name, params }; } } export default { navigate, isNavigationReady, pendingNavigation: null };
2. 修正MainApp.js的导航绑定与通知逻辑
移除所有旧版NavigationService的setTopLevelNavigator相关绑定,仅给根NavigationContainer绑定ref,同时修正通知监听逻辑:
// 移除旧NavigationService引入,替换为RootNavigation import RootNavigation, { navigationRef } from './RootNavigation'; // 其余原有import保留 // 注意:setBackgroundMessageHandler必须写在文件顶层,不能放在组件内部 messaging().setBackgroundMessageHandler(async remoteMessage => { console.log('Message handled in background!', JSON.stringify(remoteMessage)); }); export default function App(props) { const dispatch = useDispatch(); const login = useSelector(state => state.user.login); const [isAnimate, setAnimate] = useState(true); // 导航挂载完成后,执行缓存的待跳转路由 const onNavigationReady = () => { const pendingRoute = RootNavigation.pendingNavigation; if (pendingRoute) { RootNavigation.navigate(pendingRoute.name, pendingRoute.params); RootNavigation.pendingNavigation = null; } }; useEffect(() => { updateData(); // 删除之前重复注册的onNotificationOpenedApp监听,避免逻辑重复触发 }, []); const updateData = async () => { const authStatus = await messaging().requestPermission(); const enabled = authStatus === messaging.AuthorizationStatus.AUTHORIZED || authStatus === messaging.AuthorizationStatus.PROVISIONAL; if (enabled) { getFcmToken(); console.log('Authorization status:', authStatus); } messaging().onMessage(async remoteMessage => { console.log('foreground message received', JSON.stringify(remoteMessage)); }); // 应用在后台时点击通知触发 messaging().onNotificationOpenedApp(remoteMessage => { console.log('open from background state:', JSON.stringify(remoteMessage)); Alert.alert('Alert Title', 'My Alert Msg'); // 按登录态判断跳转目标,避免路由不存在 const targetRoute = login ? 'MyBooking' : 'Login'; RootNavigation.navigate(targetRoute, { redirectTo: 'MyBooking', notificationData: remoteMessage.data }); }); // 应用杀进程状态下点击通知触发 messaging() .getInitialNotification() .then(remoteMessage => { if (remoteMessage) { console.log('open from quit state:', remoteMessage.data); const targetRoute = login ? 'MyBooking' : 'Login'; RootNavigation.navigate(targetRoute, { redirectTo: 'MyBooking', notificationData: remoteMessage.data }); } }); }; // 原有getFcmToken等逻辑保持不变 return ( <NavigationContainer ref={navigationRef} onReady={onNavigationReady} > <PopUpBox okClick={() => dispatch(dispatchFunc())} /> {!login ? <NavStack /> : <NavStack2 />} </NavigationContainer> ); }
3. 补全路由跳转逻辑
- 确认登录态对应的
NavStack2中已注册名为MyBooking的页面,路由名大小写完全匹配 - 未登录状态下点击通知时先跳转登录页,将目标路由
MyBooking作为参数传递,登录成功后读取参数自动跳转至目标页即可,避免未登录时找不到MyBooking路由的报错 - 删除之前给
NavStack、NavStack2绑定ref的冗余代码,根导航容器的ref已经可以触发全量路由跳转,不需要给子栈单独绑定ref
内容的提问来源于stack exchange,提问作者Muhammad Muaz
相关产品推荐
相关产品推荐

