RN OneSignal _open 事件
RN OneSignal _open Event
OneSignal on notification open 事件在主屏幕启动后触发,然后它导航到所需的屏幕。我想检测应用程序是否在主屏幕呈现之前通过按下通知启动,这样我就可以直接导航到第二个屏幕并避免不必要地调用 api。
- "react-native-onesignal": "^3.9.3"
- "react-navigation": "^4.0.0"
代码
const _opened = openResult => {
const { additionalData, body } = openResult.notification.payload;
// how to navigate or set the initial screen depending on the payload
}
useEffect(() => {
onesignal.init();
onesignal.addEventListener('received', _received);
onesignal.addEventListener('opened', _opened);
SplashScreen.hide();
return () => {
// unsubscriber
onesignal.removeEventListener('received', _received);
onesignal.removeEventListener('opened', _opened);
}
}, []);
调试
您的问题是如何根据打开的通知负载导航或设置初始屏幕?
1) - 根据打开的通知负载设置 initial screen
。
根据 class Lifecycle useEffect
在组件输出渲染后运行,因此 useEffect
中的监听器在组件安装之前不会监听,这就是登录主屏幕的原因在登录 useEffect
之前显示,请参阅此说明。
//this the problem (NavigationContainer called before useEffect).
function App() {
useEffect(() => {}); //called second.
return <NavigationContainer>; //called first.
}
//this the solution (useEffect called Before NavigationContainer).
function App() {
const [ready, setReady] = useState(false);
//called second.
useEffect(() => {
//listen here
setReady(true);
SplashScreen.hide();
});
//called first
//no function or apis run before useEffect here it just view.
if(!ready) return <></>;// or <LoadingView/>
//called third.
return <NavigationContainer>;
}
你的代码可能是这样的
function App() {
const [ready, setReady] = useState(false);
const openedNotificationRef = useRef(null);
const _opened = openResult => {
openedNotificationRef.current = openResult.notification.payload;
}
const getInitialRouteName = () => {
if (openedNotificationRef.current) {
return "second"; //or what you want depending on the notification.
}
return "home";
}
useEffect(() => {
onesignal.addEventListener('opened', _opened);
//setTimeout(fn, 0) mean function cannot run until the stack on the main thread is empty.
//this ensure _opened is executed if app is opened from notification
setTimeout(() => {
setReady(true);
}, 0)
});
if(!ready) return <LoadingView/>
return (
<NavigationContainer initialRouteName={getInitialRouteName()}>
</NavigationContainer>
);
}
2) - navigate
取决于打开的通知负载。
首先你需要知道
A navigator needs to be rendered to be able to handle actions If you
try to navigate without rendering a navigator or before the navigator
finishes mounting, it will throw and crash your app if not handled. So
you'll need to add an additional check to decide what to do until your
app mounts.
阅读docs
function App() {
const navigationRef = React.useRef(null);
const openedNotificationRef = useRef(null);
const _opened = openResult => {
openedNotificationRef.current = openResult.notification.payload;
//remove loading screen and start with what you want.
const routes = [
{name : 'home'}, //recommended add this to handle navigation go back
{name : 'orders'}, //recommended add this to handle navigation go back
{name : 'order', params : {id : payload.id}},
]
navigationRef.current.dispatch(
CommonActions.reset({
routes : routes,
index: routes.length - 1,
})
)
}
useEffect(() => {
//don't subscribe to `opened` here
//unsubscribe
return () => {
onesignal.removeEventListener('opened', _opened);
}
}, []);
//subscribe to `opened` after navigation is ready to can use navigate
const onReady = () => {
onesignal.addEventListener('opened', _opened);
//setTimeout(fn, 0) mean function cannot run until the stack on the main thread is empty.
//this ensure _opened is executed if app is opened from notification
setTimeout(() => {
if (!openedNotificationRef.current) {
//remove loading screen and start with home
navigationRef.current.dispatch(
CommonActions.reset({
routes : [{name : 'home'}],
index: 0,
})
)
}
}, 0)
};
return (
<NavigationContainer
ref={navigationRef}
onReady={onReady}
initialRouteName={"justLoadingScreen"}>
</NavigationContainer>
);
}
对 setTimeout, CommonActions 的引用。
OneSignal on notification open 事件在主屏幕启动后触发,然后它导航到所需的屏幕。我想检测应用程序是否在主屏幕呈现之前通过按下通知启动,这样我就可以直接导航到第二个屏幕并避免不必要地调用 api。
- "react-native-onesignal": "^3.9.3"
- "react-navigation": "^4.0.0"
代码
const _opened = openResult => {
const { additionalData, body } = openResult.notification.payload;
// how to navigate or set the initial screen depending on the payload
}
useEffect(() => {
onesignal.init();
onesignal.addEventListener('received', _received);
onesignal.addEventListener('opened', _opened);
SplashScreen.hide();
return () => {
// unsubscriber
onesignal.removeEventListener('received', _received);
onesignal.removeEventListener('opened', _opened);
}
}, []);
调试
您的问题是如何根据打开的通知负载导航或设置初始屏幕?
1) - 根据打开的通知负载设置 initial screen
。
根据 class Lifecycle useEffect
在组件输出渲染后运行,因此 useEffect
中的监听器在组件安装之前不会监听,这就是登录主屏幕的原因在登录 useEffect
之前显示,请参阅此说明。
//this the problem (NavigationContainer called before useEffect).
function App() {
useEffect(() => {}); //called second.
return <NavigationContainer>; //called first.
}
//this the solution (useEffect called Before NavigationContainer).
function App() {
const [ready, setReady] = useState(false);
//called second.
useEffect(() => {
//listen here
setReady(true);
SplashScreen.hide();
});
//called first
//no function or apis run before useEffect here it just view.
if(!ready) return <></>;// or <LoadingView/>
//called third.
return <NavigationContainer>;
}
你的代码可能是这样的
function App() {
const [ready, setReady] = useState(false);
const openedNotificationRef = useRef(null);
const _opened = openResult => {
openedNotificationRef.current = openResult.notification.payload;
}
const getInitialRouteName = () => {
if (openedNotificationRef.current) {
return "second"; //or what you want depending on the notification.
}
return "home";
}
useEffect(() => {
onesignal.addEventListener('opened', _opened);
//setTimeout(fn, 0) mean function cannot run until the stack on the main thread is empty.
//this ensure _opened is executed if app is opened from notification
setTimeout(() => {
setReady(true);
}, 0)
});
if(!ready) return <LoadingView/>
return (
<NavigationContainer initialRouteName={getInitialRouteName()}>
</NavigationContainer>
);
}
2) - navigate
取决于打开的通知负载。
首先你需要知道
A navigator needs to be rendered to be able to handle actions If you try to navigate without rendering a navigator or before the navigator finishes mounting, it will throw and crash your app if not handled. So you'll need to add an additional check to decide what to do until your app mounts.
阅读docs
function App() {
const navigationRef = React.useRef(null);
const openedNotificationRef = useRef(null);
const _opened = openResult => {
openedNotificationRef.current = openResult.notification.payload;
//remove loading screen and start with what you want.
const routes = [
{name : 'home'}, //recommended add this to handle navigation go back
{name : 'orders'}, //recommended add this to handle navigation go back
{name : 'order', params : {id : payload.id}},
]
navigationRef.current.dispatch(
CommonActions.reset({
routes : routes,
index: routes.length - 1,
})
)
}
useEffect(() => {
//don't subscribe to `opened` here
//unsubscribe
return () => {
onesignal.removeEventListener('opened', _opened);
}
}, []);
//subscribe to `opened` after navigation is ready to can use navigate
const onReady = () => {
onesignal.addEventListener('opened', _opened);
//setTimeout(fn, 0) mean function cannot run until the stack on the main thread is empty.
//this ensure _opened is executed if app is opened from notification
setTimeout(() => {
if (!openedNotificationRef.current) {
//remove loading screen and start with home
navigationRef.current.dispatch(
CommonActions.reset({
routes : [{name : 'home'}],
index: 0,
})
)
}
}, 0)
};
return (
<NavigationContainer
ref={navigationRef}
onReady={onReady}
initialRouteName={"justLoadingScreen"}>
</NavigationContainer>
);
}
对 setTimeout, CommonActions 的引用。