最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to navigate from linking (deep linking with branch.io) when navigator hasn't been created yet? - Stack

programmeradmin2浏览0评论

I pretty much followed both react-navigation deep linking and branch.io react-native documentation, and either both are deprecated or just not pletely helpful.

All I want is that whenever a deep link reads from the linking, navigate to a certain screen, I'm not looking to implement a listener on a specific screen, I want this on a root path, and is either the onReady (which for me didn't work) or linking from navigator container

this is my code, very simple

const linking: LinkingOptions = {
  prefixes: ['agendameio://', '', '', ''],
  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();
    };
  },

I instantly get an error due to use navigation, but I really don't know what else to use to navigate to inside the app

EDIT: this is the error in particular

EDIT 2: I'll add my navigation so it can help to understand my problem

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"
        ponent={EmpresasScreen}
      />
      <detalleEmpresaStack.Screen
        options={{ headerShown: false }}
        name="DetalleEmpresaScreen"
        ponent={DetalleEmpresaScreen}
      />
      <agendamientoStack.Screen
        options={{ headerShown: false }}
        name="AgendamientoScreen"
        ponent={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"
        ponent={CitasScreen}
      />
      <detalleCitasStack.Screen
        options={({navigation}) => ({
          headerShown: false,
        })}
        name="DetalleCitaScreen"
        ponent={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" ponent={firstStack} />
      <tab.Screen name="Citas" ponent={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" ponent={tabStack} />
        <stack.Screen name="Error" ponent={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;

I pretty much followed both react-navigation deep linking and branch.io react-native documentation, and either both are deprecated or just not pletely helpful.

All I want is that whenever a deep link reads from the linking, navigate to a certain screen, I'm not looking to implement a listener on a specific screen, I want this on a root path, and is either the onReady (which for me didn't work) or linking from navigator container

this is my code, very simple

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();
    };
  },

I instantly get an error due to use navigation, but I really don't know what else to use to navigate to inside the app

EDIT: this is the error in particular

EDIT 2: I'll add my navigation so it can help to understand my problem

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"
        ponent={EmpresasScreen}
      />
      <detalleEmpresaStack.Screen
        options={{ headerShown: false }}
        name="DetalleEmpresaScreen"
        ponent={DetalleEmpresaScreen}
      />
      <agendamientoStack.Screen
        options={{ headerShown: false }}
        name="AgendamientoScreen"
        ponent={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"
        ponent={CitasScreen}
      />
      <detalleCitasStack.Screen
        options={({navigation}) => ({
          headerShown: false,
        })}
        name="DetalleCitaScreen"
        ponent={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" ponent={firstStack} />
      <tab.Screen name="Citas" ponent={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" ponent={tabStack} />
        <stack.Screen name="Error" ponent={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;
Share Improve this question edited Sep 1, 2021 at 18:58 Nicolas Silva asked Aug 27, 2021 at 21:26 Nicolas SilvaNicolas Silva 6492 gold badges11 silver badges31 bronze badges 2
  • We don't yet have support for react-navigation. Can you share the error message you're observing here? – Kartik Shandilya Commented Aug 30, 2021 at 7:16
  • @KartikShandilya my bad, post edited to include an image of the error, btw I want to clarify two things, both branch.io and react-navigation mention each other, and I quote react-navigation "Next, you would need to subscribe to ining links from your third-party integration, For example, to get to subscribe to ining links from branch.io:" and here from branch documentation, "Now push the view for this URL this.navigator.push({ title: title, url: url, image: image })" – Nicolas Silva Commented Aug 30, 2021 at 16:27
Add a ment  | 

3 Answers 3

Reset to default 5 +50
you can use Configuring links to open the target screen directly.

see more example here configuring-links
Here the URL /feed will open screen named Chat.

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

const linking = {
  prefixes: ['https://mychat.', '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" ponent={ChatScreen} />
        <Stack.Screen name="Profile" ponent={ProfileScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );
}
or use navigationRef.

read about it navigating-without-navigation-prop.
wait to navigation to be ready and then navigate.

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" ponent={ChatScreen} />
        <Stack.Screen name="Profile" ponent={ProfileScreen} />
      </Stack.Navigator>
    </NavigationContainer>
  );

}

can you try doing it like this

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

and do the subscribe in

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

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

this will make navigation context to be available in AppNavigator Component

Answer use custom hook 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 }
};

you can now use useNavigationWhenReady instead of 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>
  );
}

if you use react-navigation-v5 not v6 use

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


you can also show loading or splash screen while navigation not ready

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

       </RootStack>
    </NavigationContainer>
  );

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论