#javascript #typescript #react-native #deep-linking #branch.io
Вопрос:
Я в значительной степени следил как за глубокой связью с реакцией-навигацией, так и за branch.io документация react-native, и либо то, и другое устарело, либо просто не совсем полезно.
Все, что я хочу, это чтобы всякий раз, когда глубокая ссылка считывается из ссылки, переходила на определенный экран, я не собираюсь реализовывать прослушиватель на определенном экране, я хочу, чтобы это было по корневому пути и было либо включено (что для меня не сработало), либо ссылка из контейнера навигатора
это мой код, очень простой
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;
Комментарии:
1. У нас пока нет поддержки
react-navigation
. Можете ли вы поделиться сообщением об ошибке, которое вы наблюдаете здесь?2. @KartikShandilya мой плохой, пост отредактирован, чтобы включить изображение ошибки, кстати, я хочу прояснить две вещи, обе branch.io и react-навигация упоминают друг друга, и я цитирую react-навигацию «Далее, вам нужно будет подписаться на входящие ссылки из вашей сторонней интеграции, например, чтобы подписаться на входящие ссылки из branch.io:» и вот из документации филиала: «Теперь нажмите представление для этого URL this.navigator.push({ заголовок: заголовок, url: url, изображение: изображение })»
Ответ №1:
вы можете использовать ссылки настройки, чтобы напрямую открыть целевой экран.
смотрите более подробный пример здесь настройка-ссылки
Здесь 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
.
читайте об этом, перемещаясь-без-навигационной-опоры.
подождите, пока навигация будет готова, а затем перейдите.
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>
);
}
Комментарии:
1. этот тайм-аут не выглядит хорошим вариантом, не лучше ли использовать onReady из navigationContainer? хотя я не совсем уверен, как это сделать
2. я не думаю, что, setTimeout это просто, и он протестирован для меня во многих проектах , он повторяет попытки использовать навигацию в течение ограниченного времени
else if (n < 100)
, но вы также можете использовать onReady3. ну, я получаю эту ошибку «Объект» навигация » еще не инициализирован. Это может произойти, если у вас нет смонтированного навигатора» несколько строк с одной и той же ошибкой
Ответ №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
Комментарии:
1. хорошо, но что, если я захочу вызвать ссылку извне, что является хорошей практикой, имхо
2. Почему вы хотите позвонить по подписке извне?? Так как он может выполнять навигацию только тогда, когда навигация готова. Вы можете создать функцию, которая выполняет всю работу, но вам придется вызывать ее из позиции в приведенном выше коде. Или вам придется создать какой-то механизм, чтобы проверить, готова ли навигация, если не подождать несколько секунд, повторите проверку еще раз… Пока не будет готово, а затем используйте ссылку навигации для навигации
Ответ №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 amp;amp; routeName) {
if(navigationRef amp;amp; 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>
);