Pourquoi ne pas utiliser les fonctions avec état comme méthodes Vue dans l’API Options

L’utilisation de fonctions avec état sur le bloc de méthodes de l’API d’options dans Vue ne fonctionne pas comme vous pourriez vous y attendre.
Un piège courant lors du développement d’applications Vue, et l’un des plus difficiles à identifier et à déboguer ses problèmes, consiste à utiliser des fonctions avec état sur le methods
bloc de l’API d’options.
Arrière-plan
Commençons par un petit historique. Une fonction avec état est une fonction qui conserve un certain état entre les exécutions. Dans la plupart des cas, ces fonctions sont des fonctions d’ordre supérieur, c’est-à-dire des fonctions qui renvoient une fonction.
Un bon exemple de fonction avec état d’ordre supérieur, et qui est à l’origine de bon nombre de ces scénarios, est celui de Lodash debounce
. C’est une fonction utilitaire très pratique qui prend une fonction comme paramètre principal et renvoie une fonction anti-rebond.
Pour comprendre ce que signifie l’anti-rebond, comme l’explique la documentation de Lodash : « les retards invoquant [a function] jusqu’à après [certain] des millisecondes se sont écoulées depuis la dernière fois que la fonction anti-rebond a été invoquée. Ou, en termes plus simples, cela empêche qu’une fonction soit appelée à plusieurs reprises jusqu’à ce qu’un certain temps se soit écoulé.
Le composant exemple
Afin de mieux comprendre le problème, nous devons d’abord créer quelques composants et un exemple d’application pour démontrer le problème.
Reusable.vue
<template>
<p>My amazing counter</p>
<button @click="addToCounterDebounced">+</button>
State: {{ counter }}
</template>
<script>
import { debounce } from "lodash";
export default {
data: () => ({
counter: 0,
}),
methods: {
addToCounter() {
this.counter++;
},
addToCounterDebounced: debounce(function() {
this.counter++;
}, 2000),
},
};
</script>
Plongeons-y.
- Nous créons un bouton qui appelle un
addToCounterDebounce
fonction lorsque vous cliquez dessus. - Le
addToCounterDebounce
la fonction est déclarée dans lemethods
bloc de l’API d’options, et appelsdebounce
pour générer la fonction anti-rebond. - Au sein du
debounce
appel, nous créons une fonction anonyme qui augmente la valeur du composantcounter
propriété par 1. - Nous avons fixé un
2000
Minuterie anti-rebond en millisecondes (2 secondes) pour cette fonction, ce qui signifie que si cette fonction est appelée plusieurs fois dans les 2 secondes suivant le dernier appel, elle ne sera exécutée qu’une seule fois. Souviens-toi de ça ! - Enfin, nous imprimons le
counter
sous le bouton pour que nous puissions facilement le voir s’exécuter.
Le composant après avoir cliqué une fois dessus :
Même si nous cliquons sur le bouton 100 fois, l’état n’augmentera qu’une fois toutes les 2 secondes sans qu’aucune fonction ne soit appelée, c’est la nature des méthodes anti-rebond.
Si vous vous interrogez sur l’anti-rebond et quand l’utiliser, un bon cas d’utilisation des fonctions anti-rebond dans des scénarios réels consiste à retarder l’entrée de l’utilisateur lors des appels d’API.
Par exemple, un email
champ de saisie qui envoie une requête ping au serveur pour vérifier si l’e-mail est déjà utilisé. Vous ne voulez probablement pas envoyer une requête ping au serveur pour chaque une seule frappe ou modification, et je préfère attendre que l’utilisateur ait fini de taper pour déclencher la méthode.
Le problème
Si vous deviez monter ce composant dans une application et l’utiliser seul, vous ne verriez probablement aucun problème. Vous pourriez même écrire des tests unitaires et tout fonctionnerait parfaitement.
Mais que se passerait-il lorsque deux instances du même composant seraient créées ?
App.vue
<template>
<div>
<Reusable />
<Reusable />
</div>
</template>
<script>
import Reusable from "./Reusable.vue";
export default {
components: { Reusable }
};
</script>
Notez que nous avons importé le Reusable
composant que nous avons créé précédemment dans notre application et en avons créé deux instances différentes sur les lignes 3 et 4. Tout devrait fonctionner de la même manière, n’est-ce pas ? Droite?
Allez-y et essayez-le. Cliquez sur both
État +
boutons plusieurs fois dans la fenêtre de deux secondes. Nous nous attendrions les deux États pour obtenir un +1.
Hmmm… Peu importe le nombre de fois que nous cliquons sur les deux composants, seul celui sur lequel nous avons cliqué en dernier sera mis à jour. Mais pourquoi ?
Pourquoi ça ne marche pas ?
Comme nous l’avons appris au début de l’article, debounce
est une fonction avec état : elle conserve l’état interne pendant que l’exécution du composant est en cours. Lorsque nous commençons à cliquer, la fonction reçoit la référence au composant cliqué counter
propriété.
Cependant, lorsque l’on clique sur le deuxième bouton, le counter
la référence est remplacée par celle du deuxième composant counter
. La première référence est « oubliée », car la fonction attend la fin du timer pour exécuter son appel interne de this.counter++
.
La solution
Heureusement, la solution est simple et constitue également une bonne pratique à conserver pour toute utilisation de fonctions avec état et de fonctions d’ordre supérieur dans les composants Vue.
Reusable.vue
<template>
<p>My amazing counter</p>
<button @click="addToCounter">+</button>
State: {{ counter }}
</template>
<script>
import { debounce } from "lodash";
export default {
data: () => ({
counter: 0,
}),
mounted() {
this.debouncedAddToCounter = debounce(function () {
this.counter++;
}, 2000);
},
methods: {
addToCounter() {
this.counter++;
},
addToCounter() {
this.debouncedAddToCounter();
},
},
};
</script>
Jetons un coup d’oeil.
- Dans le
mounted
bloc, nous attribuons le résultat de notredebounce
appeler undebounceAddToCounter
comme propriété non réactive. Désormais, la fonction anti-rebond ne fonctionnera que dans le cadre de chaque instance. - Nous renommons le
addToCounterDebounced
àaddToCounter
pour plus de clarté, et remplacez-le sur le boutonclick
auditeur. - Dans
addToCounter
nous pouvons maintenant appelerthis.debouncedAddToCounter
.
Allez-y, essayez-le et cliquez sur les deux boutons.
Désormais, nos deux compteurs sont mis à jour séparément, comme prévu.
Conclusion
Ce piège est plus courant qu’il ne devrait l’être, donc j’espère qu’après avoir lu cet article, vous serez prêt et à l’affût d’éventuels petits bugs désagréables créés par une mauvaise utilisation des fonctions avec état dans les composants Vue.
Bon codage !
Source link