---
# System prepended metadata

title: 'Exercices: State Management with MobX'

---

# 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)

