反应导航:使用 Expo.addListener 在推送通知中导航时的不变违规
react-navigation: Invariant Violation on navigating in push notification using Expo.addListener
当我的应用程序收到推送通知时,它应该使用通知数据中的 routeName 参数导航到适当的屏幕。
这一直有效,直到我按照 react-navigation docs, and fleshed out more in a react-navigation issue here 中描述的 NavigationService 模式抽象了我们的导航。
我 运行 遇到的问题是 from react-navigation:Invariant Violation: should be set in constructor if stateful
为了解决这个问题,我尝试绕过 addListener
调用。我已经尝试了 App componentDidMount
、AppWithNavigation ref 回调(这似乎是个坏主意)和 NavigationService 的 setContent
方法。
我试过使用 NavigationService 的侦听器变体,并在应用程序中直接使用 NavigationActions.navigate
和 store.dispatch
。
我的理论是导航容器正在改变,但导航工作正常,除了应用程序关闭和通知进来时。我什至在应用程序打开时让它工作,在那种情况下它确实移动了到右边的屏幕。
我在这里错过了什么?
听众看起来像这样:
Notifications.addListener(({data}: {data: NotificationType}) => {
const {routeName, params} = data;
NavigationService.navigate({
routeName,
params,
});
});
NavigationService.js:
import {StackActions, NavigationActions} from 'react-navigation';
import type {NavigationParams, NavigationRoute} from 'react-navigation';
import {LOGOUT} from '../redux/auth';
let _container; // eslint-disable-line
function setContainer(container: Object) {
_container = container;
}
function reset(routeName: NavigationRoute, params?: NavigationParams) {
_container.dispatch(
StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName,
params,
}),
],
}),
);
}
function navigate(routeName: string, params?: NavigationParams) {
_container.dispatch(
NavigationActions.navigate({
routeName,
params,
}),
);
}
function goBack(options) {
_container.dispatch(NavigationActions.back(options));
}
function getCurrentRoute(): NavigationRoute | null {
if (!_container || !_container.state.nav) {
return null;
}
return _container.state.nav.routes[_container.state.nav.index] || null;
}
export function logOut() {
_container.dispatch({type: LOGOUT});
reset('Welcome');
}
export default {
setContainer,
navigate,
reset,
goBack,
logOut,
};
部分Navigators.js
class AppWithNavigationState extends Component {
render() {
return (
<React.Fragment>
{/* Fix for white icons on white status bar bug on iOS */}
{!isAndroid && <StatusBar barStyle="dark-content" />}
<RootStack
persistenceKey="NavigationState-2"
ref={navigatorRef => NavigatorService.setContainer(navigatorRef)}
/>
</React.Fragment>
);
}
}
export default AppWithNavigationState;
我遇到了同样的问题。我的设置与您的设置非常相似,问题是在应用程序导航器完成启动之前,我在 saga 中进行导航作为副作用。检查您是否正在使用 persistenceKey
,如果是,则之前的状态会异步水化,这就是我的问题的原因。
我在 componentDidMount
的主导航器中有一个回调 navigationReady
,但我认为我必须将其向下移动到实际屏幕以避免出现问题。目前我找不到合适的方法来订阅容器在完成恢复后调度的动作 Navigation / INIT
。
这是我最终得到的解决方案。不理想,希望尽快升级到 react-navigation 3。我正在使用 renderLoadingExperimental
在加载时呈现 SplashScreen。加载完成后我无法收到通知,因此我在我的 SplashScreen 中添加了一个 onUnmount
并在那里处理注册。
class AppWithNavigationState extends Component {
render() {
return (
<React.Fragment>
<RootStack
persistenceKey="NavigationState-2"
ref={navigatorRef => NavigationService.setContainer(navigatorRef)}
renderLoadingExperimental={() => (
<SplashScreen
source="RootStack"
onUnmount={() => {
Notifications.addListener(({data}) => {
NavigationService.navigate(data.routeName, data.params);
if (Platform.OS === 'android')
Notifications.dismissAllNotificationsAsync();
});
}}
/>
)}
/>
</React.Fragment>
);
}
}
export default AppWithNavigationState;
当我的应用程序收到推送通知时,它应该使用通知数据中的 routeName 参数导航到适当的屏幕。
这一直有效,直到我按照 react-navigation docs, and fleshed out more in a react-navigation issue here 中描述的 NavigationService 模式抽象了我们的导航。
我 运行 遇到的问题是 from react-navigation:Invariant Violation: should be set in constructor if stateful
为了解决这个问题,我尝试绕过 addListener
调用。我已经尝试了 App componentDidMount
、AppWithNavigation ref 回调(这似乎是个坏主意)和 NavigationService 的 setContent
方法。
我试过使用 NavigationService 的侦听器变体,并在应用程序中直接使用 NavigationActions.navigate
和 store.dispatch
。
我的理论是导航容器正在改变,但导航工作正常,除了应用程序关闭和通知进来时。我什至在应用程序打开时让它工作,在那种情况下它确实移动了到右边的屏幕。
我在这里错过了什么?
听众看起来像这样:
Notifications.addListener(({data}: {data: NotificationType}) => {
const {routeName, params} = data;
NavigationService.navigate({
routeName,
params,
});
});
NavigationService.js:
import {StackActions, NavigationActions} from 'react-navigation';
import type {NavigationParams, NavigationRoute} from 'react-navigation';
import {LOGOUT} from '../redux/auth';
let _container; // eslint-disable-line
function setContainer(container: Object) {
_container = container;
}
function reset(routeName: NavigationRoute, params?: NavigationParams) {
_container.dispatch(
StackActions.reset({
index: 0,
actions: [
NavigationActions.navigate({
routeName,
params,
}),
],
}),
);
}
function navigate(routeName: string, params?: NavigationParams) {
_container.dispatch(
NavigationActions.navigate({
routeName,
params,
}),
);
}
function goBack(options) {
_container.dispatch(NavigationActions.back(options));
}
function getCurrentRoute(): NavigationRoute | null {
if (!_container || !_container.state.nav) {
return null;
}
return _container.state.nav.routes[_container.state.nav.index] || null;
}
export function logOut() {
_container.dispatch({type: LOGOUT});
reset('Welcome');
}
export default {
setContainer,
navigate,
reset,
goBack,
logOut,
};
部分Navigators.js
class AppWithNavigationState extends Component {
render() {
return (
<React.Fragment>
{/* Fix for white icons on white status bar bug on iOS */}
{!isAndroid && <StatusBar barStyle="dark-content" />}
<RootStack
persistenceKey="NavigationState-2"
ref={navigatorRef => NavigatorService.setContainer(navigatorRef)}
/>
</React.Fragment>
);
}
}
export default AppWithNavigationState;
我遇到了同样的问题。我的设置与您的设置非常相似,问题是在应用程序导航器完成启动之前,我在 saga 中进行导航作为副作用。检查您是否正在使用 persistenceKey
,如果是,则之前的状态会异步水化,这就是我的问题的原因。
我在 componentDidMount
的主导航器中有一个回调 navigationReady
,但我认为我必须将其向下移动到实际屏幕以避免出现问题。目前我找不到合适的方法来订阅容器在完成恢复后调度的动作 Navigation / INIT
。
这是我最终得到的解决方案。不理想,希望尽快升级到 react-navigation 3。我正在使用 renderLoadingExperimental
在加载时呈现 SplashScreen。加载完成后我无法收到通知,因此我在我的 SplashScreen 中添加了一个 onUnmount
并在那里处理注册。
class AppWithNavigationState extends Component {
render() {
return (
<React.Fragment>
<RootStack
persistenceKey="NavigationState-2"
ref={navigatorRef => NavigationService.setContainer(navigatorRef)}
renderLoadingExperimental={() => (
<SplashScreen
source="RootStack"
onUnmount={() => {
Notifications.addListener(({data}) => {
NavigationService.navigate(data.routeName, data.params);
if (Platform.OS === 'android')
Notifications.dismissAllNotificationsAsync();
});
}}
/>
)}
/>
</React.Fragment>
);
}
}
export default AppWithNavigationState;