Kurs React Native · Moduł 3: Nawigacja
Deep Linking - Magiczne Portale
W tej lekcji8
Deep Linking to jak magiczne portale w Atlantydzie - pozwalają użytkownikom przeskakiwać bezpośrednio do określonych ekranów aplikacji z linków zewnętrznych.
Czym jest Deep Linking?
Deep Linking umożliwia:
- Otwieranie aplikacji z linków URL
- Nawigację bezpośrednio do konkretnego ekranu
- Przekazywanie parametrów przez URL
- Obsługę powiadomień push
Konfiguracja w Expo
// app.json
{
"expo": {
"scheme": "atlantyda",
"web": {
"bundler": "metro"
}
}
}Konfiguracja linking w NavigationContainer
import { NavigationContainer, LinkingOptions } from '@react-navigation/native';
import * as Linking from 'expo-linking';
// Prefiks URL dla aplikacji
const prefix = Linking.createURL('/');
type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Item: { itemId: number };
Settings: undefined;
};
const linking: LinkingOptions<RootStackParamList> = {
prefixes: [prefix, 'atlantyda://', 'https://atlantyda.app'],
config: {
screens: {
Home: '',
Profile: 'user/:userId',
Item: {
path: 'item/:itemId',
parse: {
itemId: (itemId: string) => parseInt(itemId, 10),
},
},
Settings: 'settings',
// Zagnieżdżone nawigatory
MainTabs: {
screens: {
Feed: 'feed',
Messages: 'messages/:conversationId?',
},
},
},
},
};
function App() {
return (
<NavigationContainer
linking={linking}
fallback={<LoadingScreen />}
>
<Stack.Navigator>
{/* Screens */}
</Stack.Navigator>
</NavigationContainer>
);
}Obsługiwane formaty URL
atlantyda:// → Home
atlantyda://user/123 → Profile { userId: '123' }
atlantyda://item/42 → Item { itemId: 42 }
atlantyda://settings → Settings
atlantyda://feed → MainTabs > Feed
atlantyda://messages/abc → MainTabs > Messages { conversationId: 'abc' }Tworzenie linków w aplikacji
import * as Linking from 'expo-linking';
function ShareButton({ itemId }: { itemId: number }) {
const handleShare = async () => {
// Link wewnętrzny (do aplikacji)
const internalUrl = Linking.createURL(`item/${itemId}`);
console.log(internalUrl); // exp://192.168.1.5:8081/--/item/42
// Link uniwersalny (web + aplikacja)
const universalUrl = `https://atlantyda.app/item/${itemId}`;
// Udostępnianie
await Share.share({
message: `Sprawdź ten przedmiot: ${universalUrl}`,
url: universalUrl,
});
};
return (
<Pressable onPress={handleShare}>
<Text>Udostępnij</Text>
</Pressable>
);
}Otwieranie zewnętrznych URL
import * as Linking from 'expo-linking';
function ExternalLinks() {
const openWebsite = async () => {
await Linking.openURL('https://atlantyda.app');
};
const openSettings = async () => {
// Otwiera ustawienia systemowe aplikacji
await Linking.openSettings();
};
const sendEmail = async () => {
await Linking.openURL('mailto:kontakt@atlantyda.app?subject=Pytanie');
};
const makeCall = async () => {
await Linking.openURL('tel:+48123456789');
};
const openMaps = async () => {
// iOS
await Linking.openURL('maps://app?daddr=52.2297,21.0122');
// Android
await Linking.openURL('geo:52.2297,21.0122');
};
return (
<View>
<Pressable onPress={openWebsite}><Text>Strona WWW</Text></Pressable>
<Pressable onPress={openSettings}><Text>Ustawienia</Text></Pressable>
<Pressable onPress={sendEmail}><Text>Email</Text></Pressable>
<Pressable onPress={makeCall}><Text>Zadzwoń</Text></Pressable>
</View>
);
}Nasłuchiwanie na linki
import * as Linking from 'expo-linking';
import { useEffect } from 'react';
function App() {
useEffect(() => {
// Sprawdź czy aplikacja została otwarta z linku
const getInitialURL = async () => {
const initialUrl = await Linking.getInitialURL();
if (initialUrl) {
console.log('Otwarto z linku:', initialUrl);
}
};
getInitialURL();
// Nasłuchuj na nowe linki (gdy aplikacja jest w tle)
const subscription = Linking.addEventListener('url', ({ url }) => {
console.log('Otrzymano link:', url);
});
return () => subscription.remove();
}, []);
return <NavigationContainer linking={linking}>{/* ... */}</NavigationContainer>;
}State persistence (zachowanie stanu nawigacji)
import AsyncStorage from '@react-native-async-storage/async-storage';
const NAVIGATION_STATE_KEY = 'NAVIGATION_STATE';
function App() {
const [isReady, setIsReady] = useState(false);
const [initialState, setInitialState] = useState();
useEffect(() => {
const restoreState = async () => {
try {
const savedState = await AsyncStorage.getItem(NAVIGATION_STATE_KEY);
if (savedState) {
setInitialState(JSON.parse(savedState));
}
} finally {
setIsReady(true);
}
};
restoreState();
}, []);
if (!isReady) return <LoadingScreen />;
return (
<NavigationContainer
initialState={initialState}
onStateChange={(state) => {
AsyncStorage.setItem(NAVIGATION_STATE_KEY, JSON.stringify(state));
}}
>
{/* ... */}
</NavigationContainer>
);
}Deep Linking znacząco poprawia UX aplikacji, pozwalając na udostępnianie konkretnych treści i integrację z powiadomieniami push.
Kod do tej lekcji: App.tsx
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import * as Linking from 'expo-linking';
const Stack = createNativeStackNavigator();
// Prefiks URL aplikacji, np. atlantyda://item/42
const prefix = Linking.createURL('/');
const linking = {
prefixes: [prefix],
// TODO: uzupełnij konfigurację mapowania ścieżek na ekrany:
// config: {
// screens: {
// Home: '',
// Details: 'item/:itemId',
// },
// },
};
function HomeScreen() {
return (
<View style={styles.screen}>
<Text style={styles.title}>Portal Atlantydy</Text>
</View>
);
}
function DetailsScreen({ route }) {
return (
<View style={styles.screen}>
<Text style={styles.title}>Przedmiot #{route.params?.itemId}</Text>
</View>
);
}
export default function App() {
return (
// TODO: przekaż prop linking={linking} do <NavigationContainer>
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Details" component={DetailsScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
screen: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#1a5276',
},
title: {
fontSize: 22,
color: '#fff',
fontWeight: '600',
},
});Hasła z dokumentacji w tej lekcji
Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Co definiuje właściwość 'scheme' w app.json?
2. Która metoda otwiera zewnętrzny URL lub aplikację?
Zadania praktyczne w grze
- Układanie w poziomie
Ułóż tworzenie URL do udostępnienia:
- Edytor kodu
Uzupełnij konfigurację linking.