Fermer

mars 18, 2021

Comment utiliser des machines à états finis dans React


Découvrez les machines à états finis, les avantages de ce concept informatique et comment nous pouvons l'utiliser dans les applications React.

Faire face à la logique d'état est toujours douloureux. C'est pourquoi nous sommes toujours en train de restructurer et de nous assurer que les fonctions qui mettent à jour notre état dans React fonctionnent correctement.

L'objectif de chaque développeur React lors du démarrage d'une application est, à coup sûr, de créer des fonctions d'état à jour qui ne provoquent pas d'effets secondaires inattendus dans notre application. Mais nous savons que cela arrive encore beaucoup.

Chaque jour, notre application grandit en taille – les composants sont de plus en plus gros, nous devons faire plus d'appels API, donc nous devons créer plus d'état pour gérer toutes ces données. C’est là que nous nous retrouvons piégés et commençons à créer des effets secondaires et des bogues inattendus. Gérer toutes ces données de logique d'état d'une manière simple, puissante et cohérente, tout en évitant les effets secondaires et les bogues est un défi auquel nous sommes confrontés quotidiennement.

Les machines à états finis pourraient être le bon choix pour vous en ce moment, pour résoudre les effets secondaires inattendus et maintenez votre application sans bogue pendant longtemps. Pour commencer avec les machines à états finis dans React, comprenons d'abord comment elles fonctionnent, leur but et pourquoi elles sont l'un des moyens les plus puissants de gérer la logique d'état dans une application.

Que sont les machines à états finis?

Que ce soit ou si vous n'avez pas entendu le terme «machines à états finis», nous les utilisons depuis longtemps, et pas seulement dans le calcul – dans la vraie vie aussi.

L'exemple de machine à états finis le plus courant que nous pouvons utiliser est un feu de circulation. Un feu de signalisation n'a que trois états: vert, jaune et rouge.

 États des feux de signalisation: vert, jaune, rouge

Voici comment fonctionne un feu de signalisation:

  1. Nous avons une première état vert.
  2. Nous avons une minuterie, et, après que la minuterie atteigne 30 secondes, l'état passe au jaune.
  3. Maintenant que notre état est jaune, après que la minuterie atteint 10 secondes, l'état passe à rouge.
  4. Dans notre état rouge, après que le chronomètre atteigne à nouveau 30 secondes, il changera notre état en vert.

 Flux de trafic des états: le vert pointe vers le jaune, qui pointe vers le rouge, qui pointe vers le vert.

Très simple. Nous avons un nombre fini d'états (vert, jaune et rouge), ce qui signifie que nous n'avons que trois états possibles. Il n’ya pas d’autre possibilité d’état.

Pour passer à un autre état, nous avons besoin d’une entrée. Dans notre exemple de feu de signalisation, l'entrée est notre minuterie. Chaque fois que la minuterie atteint un certain nombre de secondes, elle passe à un autre état. Ce nouvel état est notre sortie.

C'est essentiellement ainsi que fonctionne une machine à états finis.

  1. Nous avons un nombre fini d'états et un état initial.
  2. L'état ne peut changer (transition) qu'en réponse à une entrée
  3. Après le changement d'état, il produit une sortie.

 Entrée → Etat → Sortie

Avec un exemple très simple, nous pouvons comprendre comment fonctionnent les machines à états finis. Maintenant, jetez un œil à votre code. Je suis presque sûr que vous pouvez identifier très facilement quelques petites machines finies dans votre code.

Pourquoi utiliser des machines à états finis?

Vous vous demandez peut-être quels sont les avantages d'une machine à états finis, pourquoi vous devriez l'utiliser pour gérer une logique d'état complexe. Je vais énumérer quelques avantages:

  • Un nombre fini d’États. Si vous avez un nombre fini d'états, vous savez déjà à quoi ressemblera votre logique d'état et quand devriez-vous passer d'un état à un autre.
  • Modélisation visualisée. Avec les machines à états finis, vous pouvez utiliser un outil de visualisation de machine à états pour créer votre machine à états et visualiser à quoi ressemblera votre logique d'état. Il est également plus facile d'identifier les erreurs ou lorsque vous passez à un état incorrect.
  • Évitez les effets secondaires inattendus. C'est l'un des avantages les plus puissants des machines à états finis. C'est relatif au premier point, mais avec un nombre fini d'états, vous réduirez considérablement le nombre d'effets secondaires inattendus que vous créez dans votre logique d'état.
  • Relativement facile à déboguer. Le débogage d'une machine à états finis est relativement simple. Vous pouvez utiliser un outil de visualisation de la machine d'état pour cela, et cela vous fera gagner quelques heures lors du débogage.
  • Forte couverture de test. Avec un nombre fini d'états, il devient assez facile d'écrire des tests pour votre logique d'état. Si vous ne savez pas comment et vers où votre état va changer, vous pouvez éviter de nombreux tests inutiles et supprimer ces tests d'effets secondaires que nous écrivons habituellement.

Finite State Machines vs Statecharts

Statecharts was invented par David Harel, et ils sont une extension des machines d'État. Les statecharts sont plus évolutifs et cohérents que les machines à états simples, et ils sont livrés avec des fonctionnalités coûteuses pour aider des systèmes plus complexes.

L'une des principales caractéristiques des statecharts est qu'ils ont un état hiérarchique et que chaque état peut avoir des sous-états. Dans un diagramme d'états, un état qui n'a pas de sous-état est appelé un état atomique . Un état qui a un sous-état est appelé un état composé . Les autres caractéristiques intéressantes des statecharts sont les actions, les gardes, les transitions multiples et l'historique des états.

Donc, quand vous voyez quelqu'un d'autre parler de statecharts, ne vous y trompez pas – ils ne sont qu'une extension d'une machine à états finis avec un quelques fonctionnalités supplémentaires puissantes.

Maintenant que nous connaissons les machines à états et leur fonctionnement, voyons comment nous pouvons les utiliser dans nos applications React.

XState

XState est une bibliothèque JavaScript / TypeScript pour créer des machines d'état et statecharts. Cette bibliothèque est, de loin, la meilleure option de nos jours pour commencer à travailler avec des machines à états finis et des statecharts dans nos applications. Dans ce didacticiel, nous allons travailler avec XState pour React, mais cette bibliothèque a également un package pour Vue.

Alors, commençons avec XState et apprenons comment nous pouvons créer notre première machine à états finis et réaliser une meilleure niveau de logique d'état dans nos applications.

XState a un visualiseur qui nous aide à créer nos machines à états finis. Nous pouvons utiliser ce visualiseur pour voir comment notre machine à états finis fonctionne et si nous avons des erreurs. Alors, utilisons ce visualiseur pour mieux comprendre le fonctionnement de XState.

Création de notre première machine à états finis

Pour créer une machine à états finis à l’aide de XState, nous devons utiliser l’objet Machine . À l'intérieur de cet objet, nous allons créer toutes les transitions et tous les événements pour notre machine à états finis.

Appelons cette machine lightMachine et utilisons l'objet Machine :

 ] const  lightMachine  =   Machine  ( {
   ... 
} ) ; 

Chaque Machine doit avoir un id et un état initial . Nous allons donner l ' id de lightMachine et l'état initial de notre machine d'état des feux de signalisation sera vert . [19659043] const lightMachine = Machine ( {
id : 'lightMachine'
initial : 'green'
} ) ;

States

Notre état est fondamentalement une représentation de notre système: Comme les événements se produisent dans nos applications, l'état changements. Une machine à états finis ne peut être que dans un état à un instant donné; il est impossible d’être dans plus d’un.

Dans un feu de signalisation, on ne peut penser qu’à trois états possibles: vert jaune et rouge . Dans notre objet Machine nous définissons notre état en utilisant une propriété appelée states qui est également un objet. Alors, créons nos premiers états.

 const  lightMachine  =   Machine  ( {
 id :   'lightMachine' 
 initiale :   'verte' 
 États :   {
   vert :   {} 
   jaune :   {} 
   rouge :   {} 
 } 
} ) ; 

Pour l'instant, notre machine à états finis ne fait pratiquement rien. Dans chaque État, nous allons utiliser une propriété appelée le . Cette propriété changera notre état lorsqu'une transition se produira.

Voici comment cela fonctionne: Nous donnons un nom à la transition et à l'état final que nous voulons. Ainsi, par exemple, nous voulons donner le nom de JAUNE à notre transition, et nous voulons passer à l'état jaune .

Faisons de même pour les autres États , mais nous allons changer l'état final et suivre le même comportement d'un feu de signalisation. De vert à jaune de jaune à rouge de rouge à vert .

 const  lightMachine  =   Machine  ( {
 id :   'lightMachine' 
 initiale :   'verte' 
 États :   {
   vert :   {
     le :   {
       JAUNE :   'jaune' 
     } 
   } 
   jaune :   {
     le :   {
       ROUGE :   'rouge' 
     } 
   } 
   rouge :   {
     le :   {
       VERT :   'vert' 
     } 
   } 
 } 
} ) ; 

Dans notre visualiseur, voici comment notre machine à états finis est looking:

 La visualisation de notre machine à états finis "title =" La visualisation de notre machine à états finis "/></p data-recalc-dims=

En cliquant dans nos transitions, nous pouvons voir notre état changer, et notre machine à états finis fonctionne comme prévu. Un état à la fois, sans aucune erreur.

Contexte

Dans XState, nous avons quelque chose appelé Contexte. Le contexte peut être défini comme des «données quantitatives». Nous pouvons le comprendre comme des chaînes, des fonctions, objets, etc. Alors, créons notre contexte pour comprendre comment cela fonctionne.

Dans notre objet Machine sous la propriété initiale, nous allons créer un objet appelé contexte .

 contexte :   {
 mis à jour :   0 
} 

Maintenant, chaque fois que nous changeons notre état, nous allons incrémenter ce contexte de 1. Mais comment pouvons-nous faire cela? Eh bien, dans XState, nous avons quelque chose appelé Actions. Avec Actions, nous pouvons facilement envoyer des effets secondaires.

Actions

Nous allons donc créer une fonction appelée updateAction et utiliser la fonction assign pour mettre à jour notre contexte .

 const  updatedAction  =   assign  ( {
 mis à jour :   ( context  event )   =>  context .  updated  +   1 
} ) 

De plus, nous allons maintenant changer quelques choses dans notre objet Machine . Dans chaque état, nous allons passer à quelque chose comme ceci:

 vert :   {
     le :   {
       jaune :   {
         cible :   'jaune' 
         actions :   'updatedAction' 
       } 
     } 
   } 
   jaune :   {
     le :   {
       rouge :   {
         cible :   'rouge' 
         actions :   'updatedAction' 
       } 
     } 
   } 
   rouge :   {
     le :   {
       VERT :   {
         cible :   'vert' 
         actions :   'updatedAction' 
       } 
     } 
   } 

Lorsque nous avons des actions à envoyer, nous devons changer nos événements en objet et avoir deux propriétés: target est l'état suivant, et actions sont les actions que nous allons envoyer.

 const  updatedAction  =   assign  ( ] {
 mis à jour :   ( context  event )   =>  context .  updated  +   1 
} ) 
 const  lightMachine  =   Machine  ( {
 id :   'lightMachine' 
 initiale :   'verte' 
 contexte :   {
   mis à jour :   0 
 } 
 États :   {
   vert :   {
     le :   {
       JAUNE :   {
         cible :   'jaune' 
         actions :   'updatedAction' 
       } 
     } 
   } 
   jaune :   {
     le :   {
       ROUGE :   {
         cible :   'rouge' 
         actions :   'updatedAction' 
       } 
     } 
   } 
   rouge :   {
     le :   {
       VERT :   {
         cible :   'vert' 
         actions :   'updatedAction' 
       } 
     } 
   } 
 } 
} ) ; 

Utilisation dans React

Nous maintenant que notre machine à états finis fonctionne correctement, alors commençons à l'utiliser dans React et voyons comment cela fonctionne. Tout d'abord, installons quelques packages:

 yarn add xstate @ xstate / react

Maintenant, nous devrions importer l'objet Machine de xstate et le crochet useMachine de @ xstate / react .

 import   { Machine }   from   "xstate" ; 
 import   { useMachine }   from   "@ xstate / react" ; 

Dans notre composant, nous allons coller la machine à états finis que nous avons créée à l'aide du visualiseur, et également utiliser le crochet useMachine .

Le crochet useMachine est assez similaire aux autres crochets de React. L'état renvoyé est actuel et la fonction send consiste à mettre à jour notre état en utilisant nos actions. Nous allons mettre la useMachine que nous avons créée comme valeur, et également créer un nouvel objet. À l'intérieur de ce nouvel objet, nous allons créer une propriété appelée actions et y placer notre action updatedAction .

 const   [ current  send ]   =   useMachine  ( lightMachine   {
 actions :   { updatedAction } 
} ) ; 

Dans notre état actuel nous avons beaucoup de propriétés différentes . Pour l'instant, nous allons utiliser context et correspondances . Avec la propriété context nous pourrons obtenir notre context et la propriété matches est une fonction pour vérifier si notre machine à états finis est dans ce état spécifique.

Nous allons donc créer un titre pour afficher le nombre de mises à jour de notre état, et également créer trois éléments div en utilisant la propriété matches pour afficher le contenu. Nous allons comparer chaque élément div à chaque état, donc nous n'afficherons que le div de cet élément respectif.

 return   (
 < div > 
 <  h1 >  Trafic léger  < /  h1 > 
 < h1 >  Mis à jour : [19659073] { actuel .  contexte .  mis à jour }  fois  < /  h1 > 
 { current .  matches  ( 'green' )  ?   (
 < div style  =  ] { { largeur :   60  hauteur :   60  borderRadius :   " 50% " arrière-plan :  " vert " marginTop :   10  } }   / [19659261]> 
) [19659045]:   null } 
 { current .  matches  ( 'yellow' )  ?   ([19659259] < div style  =  { { largeur :   60  hauteur :   60 [19659045] borderRadius :   "50%"  background :   "yellow"  marginTop :   10  } }   / > 
) :   null } 
 { current .  matches [19659045] ( 'red' )  ?   (
 < div style  =  { { width :   60  hauteur :   60  borderRadius :   "50%"  background  :   "rouge"  marginTop : [19659127] 10  } }   / > 
) :   null } 
 < /  div  > 
) ; 

Nous allons maintenant créer trois boutons. Chaque bouton changera l'état d'une cible spécifique. Pour modifier l'état, nous utiliserons la fonction send de notre hook useMachine . Si le bouton ne correspond pas à l'état souhaité, le bouton sera désactivé.

Ainsi, par exemple, nous savons que notre premier état est vert et après cela, nous allons à jaune . Notre premier bouton aura donc le nom Jaune mais il sera désactivé s’il ne correspond pas à l’état de vert . Pour changer notre état, nous allons simplement mettre une méthode onClick et utiliser la fonction send en passant la prochaine cible qui est JAUNE . Bouton

  <
 disabled  =  {!  current .  matches  ( 'green' ) } 
 onClick  =  { ()   =>   send  ( 'YELLOW' ) } > 
  JAUNE
 < /  bouton > 

Très simple. Nous allons maintenant faire cela pour les deux autres états, et notre dernier composant ressemblera à ceci:

 const   Light   =   ()   =>   { 
 const  lightMachine  =   Machine  ( {
 id :   'lightMachine' 
 initiale :   'verte' 
 contexte :   {
   mis à jour :   0 
 } 
 États :   {
   vert :   {
     le :   {
       jaune :   {
         cible :   'jaune' 
         actions :   'updatedAction' 
       } 
     } 
   } 
   jaune :   {
     le :   {
       rouge :   {
         cible :   'rouge' 
         actions :   'updatedAction' 
       } 
     } 
   } 
   rouge :   {
     le :   {
       VERT :   {
         cible :   'vert' 
         actions :   'updatedAction' 
       } 
     } 
   } 
 } 
} ) ; 
 const  updatedAction  :  any  =   assign  ( {
 mis à jour :   ( context :  any  event :  any )   =>  context .  mis à jour  +   1 
} ) 
 const   [ current  send ] [19659138] =   useMachine  ( lightMachine   {
 actions :   { updatedAction } 
} ) ; 
 return   (
 < div > [19659259] < h1 >  Trafic léger  < /  h1 > 
 < h1 >  Mis à jour :   { actuel .  context .  updated }  times  < /  h1 > 
 { current .  matches  ( 'green' )  ?   (
 < div style  =  { { largeur :   60  hauteur :   60  borderRadius : [19659056] "50%"  fond :   "vert"  marginTop :   10  } } [19659138] / > 
) :   null } 
 { current .  matches  ( 'yellow' )  ?   (
 < div style  =  { { width :   60  height :   60  borderRadius :   "50%"  background :   "yellow"  marginTop :   10  } [19659045]}   / > 
) :   null } 
 { current .  matches  ( 'red' )  ?   (
 < div style  =  { { width :   60 [19659045] hauteur :   60  borderRadius :   "50%"  background :   "red " marginTop :   10 [1965907] 3]} }   / > 
) :   null } 
 < button disabled  =  {[19659261]!  actuel .  correspond à  ( 'green' ) }  onClick  =  { ( ])   =>   envoyer  ( 'JAUNE' ) } >  JAUNE  < /  bouton > 
 < bouton désactivé  =  {!  actuel .  correspond à  ( 'yellow' ) }  onClick  =  { ()   =>   send  ( 'RED' )  } >  RED  < /  button > 
 < button disabled  =  {!  current .  correspond à  ( 'red' ) } [1 9459004] onClick  =  { ()   =>   send  ( 'GREEN' ) } >  VERT  < /  bouton > 
 < /  div > 
) ; 
} [19659045]; 

Nous avons maintenant une application de feux de signalisation fonctionnant sous XState. C’est vraiment génial. Nous pouvons voir que notre logique est sans bogue, car nous ne pouvons pas être dans plus d'un état à la fois.

XState et les machines à états finis ont beaucoup de sens pour créer de meilleures applications lorsque vous aurez beaucoup d'états différents. Peut-être que cela prendra un certain temps pour comprendre les concepts de cette puissante bibliothèque, mais à long terme, cela vous aidera à écrire une meilleure logique d'état.

Conclusion

Dans cet article, nous en avons appris plus sur un très concept important de l'informatique connu sous le nom de machines à états finis.

Nous avons appris comment fonctionnent les machines à états, les avantages des machines à états finis par rapport à la gestion commune des états avec laquelle nous sommes habitués à travailler, et les différences entre les machines à états finis et les machines à états finis. statecharts.

Nous avons également appris comment travailler avec des machines à états finis dans les applications React en utilisant XState, une bibliothèque JavaScript / TypeScript qui nous permet de créer des machines à états finis et d'avoir une meilleure application, créant un état plus cohérent et sans bogue logique.




Source link