Développement d'applications mobiles

La principale librairie pour implémenter la navigation en React Native est React Navigation. Elle supporte les mécanismes natifs standards

  • Stack
  • Tabs
  • Drawers
  • Modals

📚 React Navigation

Mise en place

bash
npx expo install @react-navigation/native @react-navigation/native-stack react-native-screens react-native-safe-area-context
jsx
App.js
import { View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import Form from './Form.js';
import Show from './Show.js';

const Stack = createNativeStackNavigator();

export default function App() {
return (
<NavigationContainer>
<Stack.Navigator initialRouteName="Form">

<Stack.Screen
name="Form"
component={ Form }
options={{ title: 'Welcome' }}
/>

<Stack.Screen name="Show" component={ Show } />

</Stack.Navigator>
</NavigationContainer>
);
}

Pour déclencher la navigation, chaque component utilisé en tant que Screen reçoit une prop navigation. navigate(name) permet d'aller vers une nouvelle page, ou de retourner sur une page existante. Par défaut, si on est déjà sur la page, la navigation n'est pas redéclenchée.

jsx
Form.js
export default function Form({ navigation }) {

function handlePressed() {
if (display.name.trim() == '') {
// ...
} else {
navigation.navigate('Show');
}
}

// ...
}

On peut fournir des paramètre lors de la navigation.

jsx
Form.js
navigation.navigate('Show', display);
jsx
Show.js
import { View, Text, Button, Alert } from 'react-native';

export default function Show({ route }) {

console.log(`Show ${JSON.stringify(route.params)}`);
const display = route.params;

return (
<View
style={{
flex: 1, gap: 16, justifyContent: 'center', alignItems: 'center',
backgroundColor: (display.darkMode ? 'black' : 'white')
}}
>

<Text style={{ color: (display.darkMode ? 'white' : 'black') }}>
{ display.name }
</Text>

{/* ... */}
</View>
);
}

📚 Navigation Params

Plusieurs options de configuration disponibles, soit via le NavigationContainer ou directement dans les écrans enfants

  • Titre
  • Boutons
  • Style
jsx
App.js
import { StatusBar } from 'expo-status-bar';
import { View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import Form from './Form.js';
import Show from './Show.js';

const Stack = createNativeStackNavigator();

export default function App() {
return (
<>
<StatusBar style="light" />

<View style={{ flex: 1, backgroundColor: 'red' }}>
{/* View wrapper si backgroundColor dans les components enfants */}
<NavigationContainer>
<Stack.Navigator
initialRouteName="Form"
screenOptions={{
headerStyle: {
backgroundColor: 'seagreen'
},
headerTintColor: 'white',
}}
>

<Stack.Screen
name="Form"
component={ Form }
options={{ title: 'Welcome' }}
/>

<Stack.Screen name="Show" component={ Show } />

</Stack.Navigator>
</NavigationContainer>
</View>
</>
);
}
jsx
Show.js
import { StatusBar } from 'expo-status-bar';
import { View, Text, Button, Alert } from 'react-native';

export default function Show({ navigation, route }) {

console.log(`Show ${JSON.stringify(route.params)}`);
const display = route.params;

function handleAgainPressed() {
Alert.alert('Title', 'Message', [
{
text: 'Pop(to root)',
onPress: () => console.log('pressed a')
},
{
text: 'Nav(back)',
onPress: () => console.log('pressed b')
},
{
text: 'Push(new)',
onPress: () => console.log('pressed c')
},
]);
}

return (
<>
<StatusBar style={ display.darkMode ? 'dark' : 'light' } />

<View
style={{
flex: 1, gap: 16, justifyContent: 'center', alignItems: 'center',
backgroundColor: (display.darkMode ? 'black' : 'white')
}}
>

<Text style={{ color: (display.darkMode ? 'white' : 'black') }}>
{ display.name }
</Text>

<Button
title="Again?"
onPress={ handleAgainPressed }
/>

</View>
</>
);
}

📚 Stack Navigator Options

Il existe 4 principaux modes de navigation pour les Stack

  • navigate
  • push
  • pop, popToTop
  • replace
jsx
Show.js
export default function Show({ navigation, route }) {
{/* ... */}

function handleAgainPressed() {
Alert.alert('Title', 'Message', [
{
text: 'Pop(root)',
onPress: () => navigation.popToTop()
},
{
text: 'Nav(reuse)',
onPress: () => navigation.navigate('Form')
},
{
text: 'Push(new)',
onPress: () => navigation.push('Form')
},
]);
}

{/* ... */}
}

Lorsqu'on retourne sur un écran, on peut recevoir des données de navigation

jsx
Show.js
navigation.navigate('Form', { name: '' })
jsx
Form.js
import { useState, useEffect } from 'react';

export default function Form({ navigation, route }) {
{/*
VS ???
[display, setDisplay] = useState(route.params?.name)
*/
}

console.log(route.params);
console.log(display);

// Pour mettre a jour un state avec les params
useEffect(() => {
console.log(`effect ${JSON.stringify(route.params)}`);

if (route.params) {
setDisplay({ ...display, name: route.params.name });
}
}, [route.params]);

{/*
Dans le JSX
<Text>{ JSON.Stringify(display) }</Text>
*/
}
}

📚 React Navigation Params Previous SCreen

Effect

useEffect est un mécanisme de React permettant de réagir au changement d'un état, state, en vue de synchroniser avec un élément externe: requête HTTP, timeout/interval, NavigationContainer, etc.

Le second paramètre de la fonction est les dépendances déclenchant l'effet

  • , absent, chaque render
  • [], seulement 1 fois, au premier render
  • [stateA, stateB, ...], lors d'un changement d'état
jsx
Show.js
import { useEffect } from 'react';

export default function Show({ navigation, route }) {
{/* ... */}

useEffect(() => {
navigation.setOptions({
headerTintColor: display.darkMode ? 'black' : 'white',
});
}, [])

{/* ... */}
}

📚 useEffect