尚未创建导航器时如何从链接导航(使用 branch.io 进行深度链接)?

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

我非常关注 react-navigation deep linking 和 branch.io react-native 文档,要么两者都被弃用了,要么就是没有完全帮助。

我想要的是,每当深度 link 从 linking 读取时,导航到某个屏幕,我不想在特定屏幕上实现监听器,我想要这在根路径上,并且是 onReady(对我来说没用)或 linking from navigator container

这是我的代码,非常简单

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;

你能试试这样吗

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 组件中可用

您可以使用配置链接直接打开目标屏幕。

在此处查看更多示例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>
  );

}

回答使用自定义挂钩 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>
  );