# Exercices: State Management with MobX :::warning **FIRST OF ALL** Installez Mobx :) `npm install --save mobx mobx-react` or `yarn add mobx mobx-react` -- **La documentation de MobX ici** https://mobx.js.org/README.html ::: ### Exercice 1: Les bases #### 1.0 --- Set up Tout simple, dans votre fichier `App.js` ou le fichier qui sert à représenter votre App, encapsuler/entourer vos élément de la balise `<Provider>` fournit par `mobx-react`. Déclarez une constante `stores` qui sera un objet et **qui contiendra par la suite tout vos store**. Cet objet est donc **pour le moment** vide Passez cette variable `stores` au Provider comme suit: ``` html <Provider {...stores}> ``` :::info Si vous avez suivis les cours et que vous vous rappelez, `Provider` est donc une forme de context auxquels nous allons passer tout nos futures stores en "propriétés" en destructurant notre constante `stores` avec `...` Grâce à cette notation, nous pourront accéder par la suite via les props à nos différents stores. > **Exemple:** > props.nomdeMonStore.value ::: #### 1.1 --- Observable :::warning N'utilisez aucun **state** ou **useState** dans cet exercice ::: Créez un store simple dans un fichier séparé qui sera une classe que vous appelerai `Exo1Store`. * Créez une valeur `observable` nommée `text` dans cette classe Créez vous un composant `Exo1` qui affichera simplement un `Text` et un `TextInput` * Cette balise `Text` doit afficher `props.exo1Store.text` * La balise `TextInput` doit modifier cette valeur dans le store. ::: spoiler > Pour rappel vous aurez besoin de `inject` et `observer` fourni par `mobx-react` si vous avez regardé les vidéos pour que tout fonctionne. ::: ``` javascript class Exo1Store { ... } const exo1Store = new Exo1Store(); export default exo1Store; ``` ::: danger N'oublier pas d'importer `exo1Store` dans votre App.js et mettre cet élément dans notre constante `stores` créée dans l'exercice 1.0 ::: ![](https://i.imgur.com/LNiOppO.gif) #### 1.2 --- Computed Dans le store de l'exercice précédent, créez une valeur `computed` qui se nommera `textWithoutSpace`. Celle-ci porte bien son nom. Dans votre balise `<Text>` remplacez `props.exo1Store.text` par cette nouvelle valeur de sorte que, en changant `text` via votre `<TextInput>`, la valeur sans espace s'affiche au dessus. :::info Pour supprimer tout les espaces d'une chaine: ``` javascript value.replace(/ /g, '') ``` ::: ::: spoiler :angry: :angry: :angry: ``` javascript @computed get textWithoutSpace() {} ``` ::: ![](https://i.imgur.com/rwMP6YW.gif) ### Exercice 2: Flux d'authentification ::: warning Installez `react-navigation` comme dans le cours précédent <br/> `npm install @react-navigation/native ` `expo install react-native-gesture-handler react-native-reanimated react-native-screens react-native-safe-area-context @react-native-community/masked-view` `npm install @react-navigation/stacks` ::: #### 2.0 --- Créez un `StackNavigator` contenant 3 `Screen`: * SplashScreen * LoginScreen * HomeScreen La page initiale doit être le SplashScreen, ces pages sont disponibles sur **Moodle** dans l'archive **"Screens-2.0"** Vérifiez que toute les routes s'affichent correctement en changeant le paramètre `initialRouteName` du Navigator ``` html <Stack.Navigator initialRouteName="SplashScreen"> ``` #### 2.1 --- En suivant cette documentation: **https://reactnavigation.org/docs/auth-flow**, créez le début d'un flux d'authentification. Pour ça créez deux états *(useState)* au sein de votre composant qui contient votre Navigator: * **isLoading** à `true` par défaut * **isSignedIn** à `false`par défaut Tant que `isLoading` est `true` affichez le SplashScreen. Lorsque `isLoading` est `false` affichez: * La page de connexion si `isSignedIn` est `false` * La page Home si `isSignedIn` est `true` ``` javascript /* * Voici le code qui permet de simuler la fin du chargement * Ce chargement ce terminera au bout de 3 secondes */ useEffect(() => { setTimeout(() => { setIsLoading(false); }, 3000); }, []); ``` Vérifiez que tout fonctionne en changeant la valeur de `isSignedIn` manuellement dans le code. #### 2.2 --- Utilisons Mobx ::: warning Installez `AsyncStorage` comme dans le dernier cours ::: Dans cette exercice nous allons garder trace de l'authentification de l'utilisateur. **Étape 1:** * Créez un store LoginStore qui contiendra un boolean `observable` "isSignedIn" * Dans cette class créez une fonction `logInUser` qui s'occupera de changer la valeur de isSignedIn et de l'enregistrer dans le `AsyncStorage` * Créez aussi une fonction `logOutUser` qui s'occupera de changer la valeur de isSignedIn et qui enlèvera la valeur du `AsyncStorage` ou le modifiera à `false` (à vous de choisir selon votre préférence) ::: info Nous utiliseront ces fonction plus tard. Wait and See. ::: ::: warning Le second paramètre de `AsynStorage.setItem()`doit **obligatoirement** être une chaine de charactère. * Si vous utilisez donc un boolean ou autre, utilisez la méthode `JSON.stringify(value)` pour transformer votre valeur en format JSON. * Plus tard pour récupérer cette valeur comme avant, utilisez `JSON.parse(value)` ::: > **PRO TIP:*** > Vous pouvez aussi juste créer une seule fonction `signUserInOut` qui prendra un boolean en paramètre et qui s'occupera de set la valeur dans le `AsynStorage` chaque fois qu'elle est appelé. > 1. signUserInOut(false) -> set la valeur `false` et enregistre dans le AsyncStorage > 2. signUserInOut(true) -> set la valeur `true` et enregistre dans le AsyncStorage > >Magique nan ? J'espère que ça commence à percuter et que vous voyez l'intêret ! **Étape 2** ::: danger * Dans le composant App déclarez une constante `stores` qui contiendra vos stores **comme dans l'execice 1** * Entourez votre application de la balise `<Provider>` **comme dans l'execice 1** pour pouvoir accéder à vos stores dans de futur pages via les props. * Pour accéder à ce store depuis les propriété de votre composant contenant votre navigationn n'oubliez pas d'utiliser `inject` et `observer` ::: * Dans ce composant, remplacez le state `isSignedIn` créé précemment pour le remplacer par la valeur de votre store. Vérifiez que tout fonctionne en changeant la valeur de `isSignedIn` manuellement dans le code. #### 2.3 --- Automatisation Maintenant que ces étapes ont été realisé, correctement je l'espère, nous allons automatiser ce flux d'authentification avec `AsyncStorage`. Le but vous l'avez peut être compris est de se connecter en cliquant sur le bouton de login, enregistrer cette connexion dans AsyncStorage, fermer l'app, et rester connecté à notre retour. **Étape 1: Enregistrer la connexion/déconnexion** * Utiliser la fonction de votre store qui permet d'enregister la connexion dans le bouton sur la page de login * Utiliser la fonction de votre store qui permet de se déconnecter dans le bouton de la page Home ::: danger **Encore une fois**, n'oubliez pas l'utilisation de `inject` et `observer` pour accéder à votre store depuis les props ::: **Étape 2: Récupérez le storage au lancement** Notre valeur est normalement maintenant bien sauvegardé dans le `AsyncStorage`. Cependant il faut maintenant récupérer cette valeur pour savoir s'il faut afficher la page de connexion ou la page Home au lancement. * Supprimer dans votre composant qui contient le Navigatr le **setTimeOut** du useEffect fourni * Dans ce useEffect récupérer la valeur de votre `AsyncStorage`. * Si cette valeur est `false` set `isLoading` à `false` * Si cette valeur est `true` set `isLoading` à `false` et appelez votre fonction de connexion depuis votre store. > **Exemple:** ``` javascript useEffect(() => { const fetch = async () => { let isSignedIn = false; try { // Récupérer et assigner la valeur avec AsyncStorage setTimeout(() => setIsLoading(false), 500); } catch (err) { console.log(err); setTimeout(() => setIsLoading(false), 500); } if (isSignedIn) { // appeler votre fonction qui connecte le user } }; fetch(); }, []); ``` ### Exercice 3: Objet User #### 3.1 --- Cet exercice consiste à transformer notre boolean d'authentification en un objet qui représente notre utilisateur. **Étape 1: Créer un objet User** * Créez une classe `User` qui contient 3 attributs: * Une chaine de charactère `observable` `lastName` * Une chaine de charactère `observable` `firstName` * Un boolean `observable` `isSignedIn` * Dans votre store remplacez votre valeur `isSignedIn` par un `user` ``` javascript @observable user = new User() ``` * Partout où vous faisiez référence à votre précédent `isSignedIn`, faite maintenant référence au `isSignedIn` contenu dans `user` **Étape 2: Modifier vos fonction avec AsynStorage** * Dans votre fonction de connexion, sauvegardez maintenant le user dans le AsyncStorage * Dans votre fonction de déconnexion faites les changements nécessaires * Dans votre useEffect remplacez aussi la méthode de récupération ``` javascript useEffect(() => { const fetchUser = async () => { let user = null; try { user = // get item depuis AsyncStorage user = JSON.parse(user); setTimeout(() => setIsLoading(false), 500); } catch (err) { setTimeout(() => setIsLoading(false), 500); } if (user?.isSignedIn) { votreStore.user = user; // Récupère les informations du user et les assignes à l'objet user contenu dans le store //appelez votre fonction qui connecte le user } }; fetchUser(); }, []); ``` **Étape 3** * Dans la page login, changez les `onChangeText` des `<TextInput>` pour changer respectivement: `firstName` et `lastName` contenu dans le `user` * Dans la page Home, affichez ces valeurs dans les champs pré-remplis --- #### 3.2 --- Si vous arrivez jusque là * Dans la page Home, changez les `onChangeText` des `<TextInput>` pour changer respectivement: `firstName` et `lastName` contenu dans le `user` * Créez une *reaction* grâce à Mobx pour modifier le user dans le `AsyncStorage` lorsqu'un des deux champs change. ::: info Utilisez `reaction` fourni par `mobx` ``` javascript reaction( () => //ici les valeurs auxquels vous voulez réagir, () => { // ici votre réaction } ); ``` ::: ###### `Exemple:` ![](https://i.imgur.com/X1zpByF.gif)