RN OneSignal _open 事件 您的问题是如何导航或设置初始屏幕取决于打开的通知负载?2) - navigate 取决于打开的通知负载

问题描述

通知打开事件上的 Onesignal 在主屏幕启动后触发,然后导航到所需的屏幕。我想检测应用程序是否在主屏幕呈现之前按下通知启动,以便我可以直接导航到第二个屏幕并避免不必要地调用 api。

  • "react-native-onesignal": "^3.9.3"
  • “反应导航”:“^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);
      }
   },[]);

调试

enter image description here

解决方法

您的问题是如何导航或设置初始屏幕取决于打开的通知负载?

1) - 根据打开的通知负载设置 initial screen

根据 class Lifecycle useEffect 在组件输出渲染后运行,所以 useEffect 中的 listener 直到组件数量增加时才监听,这就是之前显示的登录主屏幕的原因登录 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>
    );

}

首先你需要知道

需要渲染导航器才能处理操作 如果您 尝试在不渲染导航器或在导航器之前进行导航 完成安装,如果不处理,它会抛出并使您的应用程序崩溃。所以 你需要添加一个额外的检查来决定在你的 应用挂载。

阅读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',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>
    );

}

参考 setTimeoutCommonActions