尚未创建导航器时如何从链接导航(与branch.io的深度链接)?

Posted

技术标签:

【中文标题】尚未创建导航器时如何从链接导航(与branch.io的深度链接)?【英文标题】:How to navigate from linking (deep linking with branch.io) when navigator hasn't been created yet? 【发布时间】:2021-08-27 21:26:25 【问题描述】:

我几乎都遵循了 react-navigation 深度链接和 branch.io react-native 文档,但两者都已弃用或没有完全有用。

我想要的是,每当从链接中读取深度链接,导航到某个屏幕时,我不希望在特定屏幕上实现侦听器,我希望它在根路径上,并且要么是 onReady (对我来说不起作用)或从导航器容器链接

这是我的代码,很简单

const linking: LinkingOptions = 
  prefixes: ['agendameio://', 'https://agendame.io', 'https://agendameio.app.link', 'https://agendameio.app-alternative.link'],
  subscribe(listener) 
    const navigation = useNavigation();
    const onReceiveURL = ( url :  url: string ) => listener(url);
    Linking.addEventListener('url', onReceiveURL);
    branch.skipCachedEvents();
    branch.subscribe(async ( error, params, uri ) => 
      if (error) 
        console.error('Error from Branch: ' + error);
        return;
      
      if (params) 
        DataManager.DynamicURL = params['~id'] === "951933826563912687" ? params.id : undefined;
      
      let url = params?.['+url'] || params?.['~referring_link']; // params !== undefined ? `agendameio://empresa/$params.id` : 'agendameio://empresa';
      navigation.navigate(`DetalleEmpresa$params.id`);
      listener(url);
    );
    return () => 
      Linking.removeEventListener('url', onReceiveURL);
      branch.logout();
    ;
  ,

由于使用导航,我立即收到错误消息,但我真的不知道还可以使用什么来导航到应用程序内部

编辑:这是特别的错误

编辑 2:我将添加我的导航,以便帮助理解我的问题

function firstStack() 
  return (
    <homeStack.Navigator initialRouteName="EmpresasScreen">
      <homeStack.Screen
        options=(navigation) => (
          headerShown: false,
          headerTitle: () => (
            <>
              <View style=styles.viewHeader>
                <Image 
                  resizeMode="contain" 
                  style=styles.imageLogo 
                  source=Images.iconoToolbar 
                />
              </View>
            </>
          ),
        )
        name="EmpresasScreen"
        component=EmpresasScreen
      />
      <detalleEmpresaStack.Screen
        options= headerShown: false 
        name="DetalleEmpresaScreen"
        component=DetalleEmpresaScreen
      />
      <agendamientoStack.Screen
        options= headerShown: false 
        name="AgendamientoScreen"
        component=AgendamientoScreen
      />
    </homeStack.Navigator>
  );


function secondStack() 
  return (
    <misCitasStack.Navigator>
      <misCitasStack.Screen
        options=(navigation) => (
          headerShown: false,
          headerTitle: () => (
            <>
              <View style=styles.viewHeader>
                <Image 
                  resizeMode="contain" 
                  style=styles.imageLogo 
                  source=Images.iconoToolbar 
                />
              </View>
            </>
          ),
        )
        name="MisCitasScreen"
        component=CitasScreen
      />
      <detalleCitasStack.Screen
        options=(navigation) => (
          headerShown: false,
        )
        name="DetalleCitaScreen"
        component=DetalleCitaScreen
      />
    </misCitasStack.Navigator>
  );


function tabStack() 
  return (
    <tab.Navigator
      screenOptions=(route) => (
        tabBarIcon: (focused) => 
          let iconName;
          if (route.name === 'Home') 
            iconName = focused
              ? Images.casaActive
              : Images.casa
           else if (route.name === 'Citas') 
            iconName = focused 
              ? Images.citasActive
              : Images.citas
          
          return <Image source=iconName />
        
      )
      tabBarOptions=
        showLabel: false,
      >
      <tab.Screen name="Home" component=firstStack />
      <tab.Screen name="Citas" component=secondStack />
    </tab.Navigator>
  );


function menuStackNavigator() 
  useEffect(() => 
    VersionCheck.needUpdate(forceUpdate: true).then(async res => 
      if (res.isNeeded) 
        alertNeedUpdate(res.storeUrl, false);
      
    );
    if(Platform.OS === 'android') 
      NativeModules.SplashScreenModule.hide();
    
  , [])
  return (
    <NavigationContainer linking=linking>
      <stack.Navigator headerMode="none">
        <stack.Screen name="Home" component=tabStack />
        <stack.Screen name="Error" component=ErrorScreen />
      </stack.Navigator>
    </NavigationContainer>
  );
;

const styles = StyleSheet.create(
  viewHeader: 
    alignItems: 'center',
    justifyContent: 'center',
  ,
  imageLogo: 
    alignItems: 'center',
    justifyContent: 'center',
    marginTop: 6,
    marginBottom: 6
  
);

export default menuStackNavigator;

【问题讨论】:

我们还不支持react-navigation。您可以分享您在此处观察到的错误消息吗? @KartikShandilya 我的错,帖子已编辑以包含错误的图像,顺便说一句,我想澄清两件事,branch.io 和 react-navigation 都相互提及,我引用 react-navigation “接下来,您需要订阅来自第三方集成的传入链接,例如,要订阅来自 branch.io 的传入链接:”和此处来自分支文档,“现在推送此 URL 的视图 this.navigator .push( title: title, url: url, image: image )" 【参考方案1】:
您可以使用配置链接直接打开目标屏幕。

在此处查看更多示例configuring-links 此处 URL /feed 将打开名为 Chat 的屏幕。

import  NavigationContainer  from '@react-navigation/native';

const linking = 
  prefixes: ['https://mychat.com', 'mychat://'],
  config: 
    screens: 
      Chat: 'feed/:sort', //URL `/feed` will open screen named `Chat`.
      Profile: 'user',
    
  ,
;

function App() 
  return (
    <NavigationContainer linking=linking fallback=<Text>Loading...</Text>>
      <Stack.Navigator>
        <Stack.Screen name="Chat" component=ChatScreen />
        <Stack.Screen name="Profile" component=ProfileScreen />
      </Stack.Navigator>
    </NavigationContainer>
  );

或使用navigationRef

阅读它navigating-without-navigation-prop。 等待导航准备好,然后导航。

import  createNavigationContainerRef  from '@react-navigation/native';

function App() 

  const navigationRef = createNavigationContainerRef();

  const navigateWhenNavigationReady = (routeName, params, n = 0) => 
    setTimeout(() => 
        if (navigationRef?.getRootState()) 
            navigationRef.navigate(routeName, params)
        else if (n < 100) 
            navigateWhenNavigationReady(routeName, params, n + 1);
        
    , 300)
  

  const linking = 
     ...,
     subscribe(listener) 
        ...
        navigateWhenNavigationReady("Chat", id: 123);
     
  ;


  
  return (
    <NavigationContainer ref=navigationRef linking=linking>
      <Stack.Navigator>
        <Stack.Screen name="Chat" component=ChatScreen />
        <Stack.Screen name="Profile" component=ProfileScreen />
      </Stack.Navigator>
    </NavigationContainer>
  );



【讨论】:

超时看起来不是一个好的选择,使用navigationContainer中的onReady不是更好吗?虽然我不完全确定该怎么做 我不这么认为,setTimeout 很简单,在很多项目中都为我测试过,它会在有限的时间内重试使用导航else if (n &lt; 100),但你也可以使用onReady 好吧,我收到此错误“'导航'对象尚未初始化。如果您没有安装导航器,则可能会发生这种情况”多行相同的错误跨度> 【参考方案2】:

你可以试试这样吗

const App = () => 
  return (
    <NavigationContainer>
      <AppNavigator />
    </NavigationContainer>
  );
;

然后订阅

export const AppNavigator = () => 
  const navigation =useNavigation();
 
  useEffect(()=>
  //add listener and navigation logic here

  ,[]);
 
  return (
    <Stack.Navigator >
      ...
    </Stack.Navigator>
  );
;

这将使导航上下文在 AppNavigator 组件中可用

【讨论】:

好的,但是如果我想从外部调用链接怎么办,恕我直言,这是一个很好的做法 为什么要从外面调用subscribe??因为它只能在导航准备好时进行导航。您可以创建一个函数来完成所有工作,但必须从上面代码中的位置调用它。或者你将不得不创建一些机制来检查导航是否准备好如果不等待几秒钟再做检查......直到准备好然后使用导航参考来导航【参考方案3】:

回答使用自定义钩子useNavigationWhenReady

const useNavigationWhenReady = (isReady, navigationRef) => 

    const [routeName, setRouteName] = React.useState();
    const [routeParams, setRouteParams] = React.useState();
    const [navigationAction, setNavigationAction] = React.useState("navigate");

    React.useEffect(() => 
        if (isReady && routeName) 
            if(navigationRef && navigationRef[navigationAction]) 
                const _navigationAction = navigationRef[navigationAction];
                _navigationAction(routeName, routeParams);
            
        
    , [isReady, routeParams, routeParams]);


    const navigate = (_routeName, _routeParams = ) => 
        if(!routeName) 
           setNavigationAction("navigate");
           setRouteParams(_routeParams);
           setRouteName(_routeName);
        
    ;

    const reset = (state) => 
        if(!routeName) 
           setNavigationAction("reset");
           setRouteName(state);
        
    ;

    return  navigate, reset 
;

您现在可以使用useNavigationWhenReady 代替useNavigation

import  createNavigationContainerRef  from '@react-navigation/native';

function App() 

  const [isReady, setReady] = React.useState(false);  
  const navigationRef = createNavigationContainerRef();

  //define it here
  const navigation = useNavigationWhenReady(isReady, navigationRef);

  const handleOpenNotificationOrOpenLinking = () => 
     ...
     navigation.navigate("screenName", param1: value1); 
     //or use reset
     //navigation.reset(
        //index: 1,
        //routes: [ name: 'screenName' ]
      //);
  ;

  return (
    <NavigationContainer ref=navigationRef onReady=() => setReady(true)>

    </NavigationContainer>
  );

如果你使用 react-navigation-v5 而不是 v6 使用

//const navigationRef = createNavigationContainerRef();
//const navigation = useNavigationWhenReady(isReady, navigationRef);
const navigationRef = React.useRef();
const navigation = useNavigationWhenReady(isReady, navigationRef?.current);


您还可以在导航未准备好时显示加载或启动屏幕

return (
    <NavigationContainer ref=navigationRef onReady=() => setReady(true)>
        <RootStack.Navigator initialRouteName=isReady ? "home" : "loading" >

       </RootStack>
    </NavigationContainer>
  );

【讨论】:

以上是关于尚未创建导航器时如何从链接导航(与branch.io的深度链接)?的主要内容,如果未能解决你的问题,请参考以下文章

如何从活动导航链接中删除悬停时不必要的附加下划线?

如何在构建时使用占位符和导航组件创建 Deeplink

从直接链接导航时,Nuxt.js `this.$route.params.id` 未定义

链接与导航

在 Swift 中清除导航堆栈

重新创建应用时,片段与底部导航视图图标不匹配