Fermer

octobre 29, 2020

Composants d'ordre supérieur: un modèle de conception d'application React


Cet article a été rédigé par l'auteur invité Jack Franklin . Les messages d'invités de SitePoint visent à vous proposer du contenu attrayant rédigé par d'éminents écrivains et orateurs de la communauté JavaScript

Dans cet article, nous verrons comment utiliser des composants d'ordre supérieur pour garder vos applications React ordonnées, bien structurées et faciles à entretenir . Nous discuterons de la manière dont les fonctions pures maintiennent le code propre et comment ces mêmes principes peuvent être appliqués aux composants React.

Pure Functions

Une fonction est considérée comme pure si elle adhère aux propriétés suivantes:

  • toutes les données traite est déclaré comme arguments
  • il ne mute pas les données qui lui ont été données ou toute autre donnée (on les appelle souvent effets secondaires )
  • étant donné la même entrée, il toujours renvoie la même sortie.

Par exemple, la fonction add ci-dessous est pure:

 function   add  ( x  y )   {
   return  x  +  y ; 
} 

Cependant, la fonction badAdd ci-dessous est impur:

 let  y  =   2 ; 
 function   badAdd  ( x )   {
   retour  x  +  y ; 
} 

Cette fonction n'est pas pure car elle référence des données qui ne lui ont pas été directement fournies. Par conséquent, il est possible d'appeler cette fonction avec la même entrée et d'obtenir une sortie différente:

 let  y  =   2 ; 
 badAdd  ( 3 ) 
y  =   3 ; 
 badAdd  ( 3 )  

Pour en savoir plus sur les fonctions pures, vous pouvez lire " Une introduction à raisonnablement pure programmation ”par Mark Brown.

Alors que les fonctions pures sont très utiles et facilitent le débogage et le test d'une application, il vous faudra parfois créer des fonctions impures qui ont des effets secondaires, ou modifier le comportement d'une application existante fonction à laquelle vous ne pouvez pas accéder directement (une fonction d'une bibliothèque, par exemple). Pour cela, nous devons examiner les fonctions d'ordre supérieur.

Fonctions d'ordre supérieur

Une fonction d'ordre supérieur est une fonction qui renvoie une autre fonction lorsqu'elle est appelée. Souvent, ils prennent aussi une fonction comme argument, mais ce n'est pas nécessaire pour qu'une fonction soit considérée comme d'ordre supérieur.

Disons que nous avons notre fonction add d'en haut, et nous voulons écrire du code pour que lorsque nous l'appelons, nous enregistrons le résultat dans la console avant de renvoyer le résultat. Nous ne pouvons pas modifier la fonction add nous pouvons donc créer une nouvelle fonction à la place:

 function   addAndLog  ( x  y [19659011])   {
   const  result  =   add  ( x  y ) ; 
   console .  log  ( ` Result:  $ { result } "  ) ; 
   return  result ; 
} 

Nous décidons que la journalisation des résultats des fonctions est utile, et maintenant nous voulons faire de même avec une fonction soustraire . Plutôt que de dupliquer ce qui précède, nous pourrions écrire une fonction d'ordre supérieur qui peut prendre une fonction et retourner une nouvelle fonction qui appelle la fonction donnée et enregistre le résultat avant de la renvoyer:

 function   ] logAndReturn  ( func )   {
   return   function  (  ...  args )   {
     const  result  =   func  ( ...  args ) 
     console .  log  ( 'Result'  result ) ; 
     return  result ; 
  } 
} 

Maintenant, nous pouvons prendre cette fonction et utilisez-la pour ajouter la journalisation à add et soustraire :

 const  addAndLog  =   logAndReturn  ( add ) ; 
 addAndLog  ( 4 [19659019] 4 )  

 const  soustractAndLog  =   logAndReturn  ( soustract ) ; 
 soustractAndLog  ( 4   3 )  

logAndReturn est une fonction d'ordre supérieur car elle prend une fonction comme argument et renvoie une nouvelle fonction que nous pouvons appeler. Celles-ci sont vraiment utiles pour encapsuler des fonctions existantes dont vous ne pouvez pas modifier le comportement. Pour plus d'informations à ce sujet, consultez l'article de M. David Green « Higher-Order Functions in JavaScript », qui entre beaucoup plus en détail sur le sujet.

See the Pen
Higher Order Functions
par SitePoint ( @SitePoint )
sur CodePen .

Composants d'ordre supérieur

En entrant dans React land, nous pouvons utiliser la même logique que ci-dessus pour prenez les composants React existants et donnez-leur des comportements supplémentaires.

Remarque: avec l'introduction de React Hooks publié dans React 16.8, les fonctions d'ordre supérieur sont devenues légèrement moins utiles car les hooks ont permis le partage de comportement sans avoir besoin de composants supplémentaires. Cela dit, ils restent un outil utile à avoir dans votre ceinture.

Dans cette section, nous allons utiliser React Router la solution de routage de facto pour React. Si vous souhaitez vous familiariser avec la bibliothèque, je recommande vivement la documentation React Router comme meilleur endroit pour commencer.

Composant Link de React Router

React Router fournit un composant utilisé pour créer des liens entre les pages d'une application React. L'une des propriétés que prend ce composant est activeClassName . Lorsqu'un possède cette propriété et qu'il est actuellement actif (l'utilisateur est sur une URL vers laquelle le lien pointe), le composant recevra cette classe, ce qui permettra au développeur de le styliser.

C'est une fonctionnalité vraiment utile , et dans notre application hypothétique, nous décidons que nous voulons toujours utiliser cette propriété. Cependant, après cela, nous découvrons rapidement que cela rend tous nos composants très verbeux:

 < NavLink   to  =  " / " [19659134] activeClassName  =  " active-link "  >  Accueil  </  NavLink  > [19659145] < NavLink   to  =  " / about "   activeClassName  =  " active-link "  >  À propos de  </  NavLink  > 
 < NavLink   to  =  " / contact  "  activeClassName  = "  active-link  " >  Contact  </  NavLink  >  

Notez que nous devons répéter la propriété de nom de classe à chaque fois. Non seulement cela rend nos composants verbeux, mais cela signifie également que si nous décidons de changer le nom de la classe, nous devons le faire à de nombreux endroits.

Au lieu de cela, nous pouvons écrire un composant qui encapsule le composant:

 const   AppLink   =   ( props )   =>   {
   return   (
     < NavLink   to  =  { props .  to }   activeClassName  =  " active-link  " > 
       { accessoires .  enfants }  
     </  NavLink  >  
  ) ; 
} ; 

Et maintenant nous pouvons utiliser ce composant, qui met en ordre notre liens:

 < AppLink   to  =  " / home "   exact >  Home  </ [19659133] AppLink  > 
 < AppLink   to  =  " / about "  >  About [19659142] </  AppLink  > 
 < AppLink   to  =  " / contact "   >  Contact  </  AppLink  >  

Dans l'écosystème React, ces composants sont appelés composants d'ordre supérieur , car ils prennent un composant existant et le manipulent légèrement sans changer le composant existant . Vous pouvez également les considérer comme des composants wrapper, mais vous les trouverez communément appelés composants d'ordre supérieur dans le contenu basé sur React.

Meilleurs composants d'ordre supérieur

Le composant ci-dessus fonctionne, mais nous pouvons le faire. beaucoup mieux. Le composant que nous avons créé n'est pas tout à fait adapté.

Accepter plusieurs propriétés

Le composant attend deux propriétés:

  • this.props.to qui est l'URL vers laquelle le lien doit rediriger l'utilisateur vers
  • this.props.children qui est le texte présenté à l'utilisateur

Cependant, le composant accepte beaucoup plus de propriétés, et là peut être un moment où vous souhaitez transmettre des propriétés supplémentaires avec les deux ci-dessus, que nous voulons presque toujours passer. Nous n'avons pas rendu très extensible en codant en dur les propriétés exactes dont nous avons besoin.

Le spread JSX

JSX, la syntaxe de type HTML que nous utilisons pour définir les éléments React, prend en charge l'opérateur de diffusion pour transmettre un objet à un composant en tant que propriétés. Par exemple, les exemples de code ci-dessous permettent d'obtenir la même chose:

 const  props  =   { a :   1  b  ]:   2 } ; 
 < Foo   a  =  { props .  a }   b  =  { accessoires .  b }    /> 

 < Foo   { ] ...  props }    />  

Using {... props} étend chaque clé de l'objet et la passe à Foo en tant que propriété individuelle.

Nous pouvons utiliser cette astuce avec donc nous supportons toute propriété arbitraire supportée par . En faisant cela, nous nous prémunissons également pour l'avenir; si ajoute de nouvelles propriétés dans le futur, notre composant wrapper les prendra déjà en charge.

 const   AppLink   =   ( props )   =>   ] {
   return   < NavLink   { ...  props }   activeClassName  =  " active-link  "   />  ; 
} 

Maintenant acceptera toutes les propriétés et les transmettra. Notez que nous pouvons également utiliser la forme à fermeture automatique au lieu de référencer explicitement {props.children} entre les balises . React permet de passer children comme accessoire normal ou comme élément enfant d'un composant entre la balise d'ouverture et de fermeture.

Ordre des propriétés dans React

Imaginez que pour un lien spécifique sur votre page, vous devez utiliser un autre activeClassName . Vous essayez de le transmettre dans car nous transmettons toutes les propriétés via:

 < AppLink   to  =  " / special-link "   activeClassName  =  " special-active "  >  Special Secret Link  </  AppLink  >  

Cependant, cela ne fonctionne pas. La raison en est l'ordre des propriétés lorsque nous rendons le composant :

 return   < Link   { ...  props } [19659134] activeClassName  =  " active-link "    />  ; 

Lorsque vous avez la même propriété plusieurs fois dans un composant React , la dernière déclaration l'emporte . Cela signifie que notre dernière déclaration activeClassName = "active-link" l'emportera toujours, puisqu'elle est placée après {... this.props} . Pour résoudre ce problème, nous pouvons réorganiser les propriétés afin de diffuser this.props en dernier. Cela signifie que nous définissons des valeurs par défaut raisonnables que nous aimerions utiliser, mais que l'utilisateur peut les remplacer s'il a vraiment besoin de:

 return   < Link  activeClassName  =  "active-link"   { ...  props }    / > ; 

En créant des composants d'ordre supérieur qui enveloppent les composants existants mais avec comportement supplémentaire, nous gardons notre base de code propre et nous défendons contre les changements futurs en ne répétant pas les propriétés et en conservant leurs valeurs à un seul endroit.

Créateurs de composants d'ordre supérieur

Souvent, vous aurez un certain nombre de composants que vous ' Vous devrez adopter le même comportement. Ceci est très similaire au début de cet article lorsque nous avons encapsulé ajouter et soustraire pour y ajouter une journalisation.

Imaginons que dans votre application vous ayez un objet contenant des informations sur le utilisateur actuel authentifié sur le système. Vous avez besoin de certains de vos composants React pour pouvoir accéder à ces informations, mais plutôt que de les rendre aveuglément accessibles pour chaque composant que vous voulez être plus strict quant aux composants qui reçoivent les informations.

La façon de résoudre ce problème est de créer un fonction que nous pouvons appeler avec un composant React. La fonction retournera alors un nouveau composant React qui rendra le composant donné mais avec une propriété supplémentaire qui lui donnera accès aux informations utilisateur.

Cela semble assez compliqué, mais c'est rendu plus simple avec du code:

 function   wrapWithUser  (  Component  )   {
  
   const  secretUserInfo  =   {
    nom :   'Jack Franklin' 
    favouriteColour :   'blue' 
  } ; 

  
  
   return   function  ( props )   {
    
    
     return   < Utilisateur du composant    =  { secretUserInfo }   { ...  accessoires }    / >  
  } 
} 

La ​​fonction prend un composant React (qui est facile à repérer étant donné que les composants React doivent avoir des lettres majuscules au début) et renvoie une nouvelle fonction qui rendra le composant qu'il était donné avec une propriété supplémentaire de user qui est défini sur secretUserInfo .

Prenons maintenant un composant, qui veut accéder à ces informations pour pouvoir les afficher l'utilisateur connecté:

 const   AppHeader   =   function  ( props )   {
   if   ( props .  utilisateur ) [19659014] {
     return   < p >  Connecté en tant que  { props .  user .  name [19659011]}  </  p >  ; 
  }   else   {
     return   < p  >  Vous devez vous connecter  </  p >  ; 
  } 
} 

La ​​dernière étape consiste à connecter ce composant pour qu'il soit donné this.props.user . Nous pouvons créer un nouveau composant en passant celui-ci dans notre fonction wrapWithUser :

 const   ConnectedAppHeader   =   wrapWithUser  ( AppHeader ) [19659011]; 

Nous avons maintenant un composant qui peut être rendu et nous aurons accès à l'objet user .

J'ai choisi d'appeler le composant ConnectedAppHeader parce que je pense qu'il est connecté à des données supplémentaires auxquelles tous les composants n'ont pas accès.

Ce modèle est très courant dans les bibliothèques React, en particulier dans Redux donc être conscient de son fonctionnement et des raisons pour lesquelles il est utilisé vous aidera à mesure que votre application se développera et que vous dépendez d'autres bibliothèques tierces qui utilisent cette approche.

Conclusion

Cet article a montré comment , en appliquant les principes de la programmation fonctionnelle tels que les fonctions pures et les commandes d'ordre supérieur ponents de React, vous pouvez créer une base de code plus facile à gérer et à utiliser au quotidien.

En créant des composants d'ordre supérieur, vous pouvez conserver les données définies à un seul endroit, ce qui facilite la refactorisation. Les créateurs de fonctions d'ordre supérieur vous permettent de garder la plupart des données privées et n'exposent que des éléments de données aux composants qui en ont vraiment besoin. En faisant cela, vous indiquez clairement quels composants utilisent quels bits de données, et au fur et à mesure que votre application grandit, vous trouverez cela bénéfique.

Si vous avez des questions, j'aimerais les entendre. N'hésitez pas à m'envoyer un ping @Jack_Franklin sur Twitter.






Source link