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

#### 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() {}
```
:::

### 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:`
