Navigation avec le hook router d'Expo Router (version 2)

Mise en ligne : dimanche 7 janvier 2024

image de Navigation avec le hook router d'Expo Router (version 2)}

1. Avant de commencer

Cet article est la deuxième partie d'une série qui est consacrée à la maîtrise d'Expo Router, dans sa deuxième version. Voici deux liens qui peuvent vous être utiles :

2. Pourquoi utiliser ce hook plutôt qu'un composant Link ?

Vous pourriez, par exemple, avoir besoin d'aller sur une autre page, après avoir réalisé une action dans une fonction, et dans ce cas, l'utilisation du hook router est nécessaire. Son utilisation est d'ailleurs très similaire au hook useRouter de Next.js Pour pouvoir tester sur le navigateur, nous allons modifier la propriété webdu fichier app.json, en ajoutant la ligne "bundler": "metro" :

app.json
"web": { "bundler": "metro", "favicon": "./assets/favicon.png" },

Lançons notre serveur web avec la commande npm run web

Nous allons nous amuser dans la page settings

3. router.push

Le push rajoute une nouvelle page à la stackactuelle, ce qui signifie que l'on pourra revenir en arrière, à l'aide des boutons de redirections de notre navigateur. Modifions notre page settings:

app/settings/index.tsx
import { router } from 'expo-router'; import { View, Text, StyleSheet, Button } from 'react-native'; export default function SettingsScreen() { const handlePush = () => { router.push('/'); }; return ( <View style={styles.container}> <Text style={styles.title}>Settings</Text> <Button title="Push" onPress={handlePush} /> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', padding: 24, gap: 12, }, title: { fontSize: 32, fontWeight: 'bold', }, });

Ce bouton permet de revenir sur la page d'accueil, mais vu que nous avons utiliser push, ça rajoute notre page à la stack actuelle. Nous pouvons également transmettre un objet contenant le pathnameet les params, comment nous le ferions avec un composant Link. Par exemple, un bouton pourrait renvoyer vers une page de blog avec un slug :

app/settings/index.tsx
const handlePushWithParams = () => { router.push({ pathname: '/blog/[slug]', params: { slug: 'super-article-de-blog' }, }); };
app/settings/index.tsx
<Button title="Push avec params" onPress={handlePushWithParams} />

4. router.replace

Le replace remplace la route actuelle dans la stack, ce qui signifie, bien évidemment, que vous ne pourrez pas revenir en arrière dans votre navigation, ça peut être utile, par exemple, après une authentification. Créons une nouvelle fonction handleReplace avec le bouton associé :

app/settings/index.tsx
const handleReplace = () => { router.replace('/'); };
app/settings/index.tsx
<Button title="Replace" onPress={handleReplace} />

Si, on click sur notre nouveau bouton, on constate bien que notre nouvelle route remplace l'ancienne.

5. router.back

Nous pouvons implémenter le retour sur la page précédente avec back: Ajoutons la fonction handleBack et le bouton associé :

app/settings/index.tsx
const handleBack = () => { router.back(); };
app/settings/index.tsx
<Button title="Replace" onPress={handleReplace} />

Désormais, nous avons un bouton qui permet de revenir en arrière. Affinons un peu notre design pour avoir un vrai bouton Back. Modifions notre bouton :

app/settings/index.tsx
<Pressable onPress={handleBack} style={styles.buttonBack}> <Ionicons name="arrow-back" size={24} color="black" /> </Pressable>

Ajoutons le style dans le StyleSheet.create, en ajoutant également la position absolute dans le container :

app/settings/index.ts
const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', padding: 24, gap: 12, position: 'relative', }, title: { fontSize: 32, fontWeight: 'bold', }, buttonBack: { position: 'absolute', top: 12, left: 12, }, });

N'oublions pas d'ajouter notre import pour utiliser notre icone :

app/settings/index.tsx
import { Ionicons } from '@expo/vector-icons';

C'est beaucoup mieux comme ça 😉.

6. router.canGoBack

Nous pouvons vérifier que nous pouvons revenir en arrière, pour par exemple, cacher ou désactiver un bouton, si l'action de retour n'est pas possible. Pour tester cette fonctionnalité, nous allons externaliser notre composant BackButton, en créant un bouton réutilisable.

components/back-button.tsx
import { Pressable, StyleSheet } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { router } from 'expo-router'; export default function BackButton() { const handleBack = () => { router.back(); }; return ( <Pressable onPress={handleBack} style={styles.buttonBack}> <Ionicons name="arrow-back" size={24} color="black" /> </Pressable> ); } const styles = StyleSheet.create({ buttonBack: { position: 'absolute', top: 24, left: 24, }, });

Importons ce bouton dans la page de settings, mais également dans la page d'accueil. Modifions notre bouton, pour prendre en charge le canGoBack :

components/back-button.tsx
import { Pressable, StyleSheet } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { router } from 'expo-router'; export default function BackButton() { const handleBack = () => { router.back(); }; return ( <Pressable disabled={!router.canGoBack()} onPress={handleBack} style={styles.buttonBack}> <Ionicons name="arrow-back" size={24} color={router.canGoBack() ? 'black' : 'lightgray'} /> </Pressable> ); } const styles = StyleSheet.create({ buttonBack: { position: 'absolute', top: 24, left: 24, }, });

Dans la page d'accueil, pensez bien à modifier les styles du container, en ajoutant la propriété `"position":"absolute", et en mettant le bouton dans la première View. On voit bien que lorsqu'il n'est pas possible de revenir en arrière, le bouton est grisé et non clickable.

7. router.setParams

Le setParams permet de gérer les query de nos urls. Dans notre page de settings, nous pourrions ajoutons une gestion du darkmode. Créons un type pour gérer les différents états :

app/settings/index.tsx
type Mode = "light" | "dark"

Nous utiliserons le hook useGlobalSearchParams pour avoir accès aux searchParams.

app/settings/index.tsx
import { useGlobalSearchParams } from 'expo-router';
app/settings/index.tsx
const { mode } = useGlobalSearchParams();

Nous créons ensuite une fonction switchModequi nous permettra de changer de mode :

app/settings/index.tsx
const switchMode = () => { const newMode = mode === 'light' ? 'dark' : 'light'; router.setParams({ mode: newMode }); };

Nous allons Créer un Pressable qui s'adpatera au contexte (affichage et action).

app/settings/index.tsx
<Pressable onPress={switchMode}> <Text> {`Switch to ${mode === 'light' ? 'Dark' : 'Light'} Mode`} </Text> </Pressable>

Nous voyons bien que tout fonctionne correctement :

  • Les searchParamsde notre url sont bien modifiés.
  • Le texte du bouton change bien suivant l'état de notre url.
  • C'est bien la bonne action qui est appelé, suivant le mode actuel.

Il est important de noté, que setParams agit comme un state, et effectue un rerender du composant.