Catégories
Astuces et Design

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

Cet article est 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 verrons comment les fonctions pures maintiennent le code propre et comment ces mêmes principes peuvent être appliqués aux composants React.

Fonctions pures

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

  • toutes les données qu'il traite sont déclarées en arguments
  • il ne modifie pas les données qui lui ont été fournies ni aucune autre donnée (souvent appelées Effets secondaires)
  • étant donné la même entrée, il sera toujours renvoie la même sortie.

Par exemple, le add la fonction 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) {
  return x + y;
}

Cette fonction n'est pas pure car elle fait 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 à la programmation raisonnablement pure» de Mark Brown.

Alors que les fonctions pures sont très utiles et rendent le débogage et le test d'une application beaucoup plus faciles, vous devrez parfois créer des fonctions impures qui ont des effets secondaires, ou modifier le comportement d'une fonction existante à laquelle vous ne pouvez pas accéder directement (une fonction d'une bibliothèque, par exemple). Pour activer 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 également une fonction comme argument, mais cela n’est pas nécessaire pour qu’une fonction soit considérée comme d’ordre supérieur.

Disons que nous avons notre add fonction 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 le add fonction, donc à la place, nous pouvons créer une nouvelle fonction:

function addAndLog(x, y) {
  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 un subtract une fonction. Plutôt que de dupliquer ce qui précède, nous pourrions écrire un 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 le retourner:

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

Maintenant, nous pouvons utiliser cette fonction et l'utiliser pour ajouter la journalisation à add et subtract:

const addAndLog = logAndReturn(add);
addAndLog(4, 4) 

const subtractAndLog = logAndReturn(subtract);
subtractAndLog(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 «Fonctions d’ordre supérieur en JavaScript», qui donne beaucoup plus de détails sur le sujet.

Voir le stylo
Fonctions d'ordre supérieur 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 prendre les composants React existants et leur donner 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 permettaient le partage de comportement sans avoir besoin de composants supplémentaires. Cela dit, ils sont toujours 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 de 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 cette le composant prend est activeClassName. Lorsqu'un possède cette propriété et est actuellement active (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="/" activeClassName="active-link">HomeNavLink>
<NavLink to="/about" activeClassName="active-link">AboutNavLink>
<NavLink to="/contact" activeClassName="active-link">ContactNavLink>

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 dans 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">
      {props.children}
    NavLink>
  );
};

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

<AppLink to="/home" exact>HomeAppLink>
<AppLink to="/about">AboutAppLink>
<AppLink to="/contact">ContactAppLink>

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.

De meilleurs composants d'ordre supérieur

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

Accepter plusieurs propriétés

le Le composant attend deux propriétés:

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

Cependant, le Le composant accepte beaucoup plus de propriétés, et il peut y avoir un moment où vous voudrez passer des propriétés supplémentaires avec les deux ci-dessus, que nous voulons presque toujours passer. Nous n'avons pas fait très extensible en codant en dur les propriétés exactes dont nous avons besoin.

La diffusion JSX

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

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

<Foo {...props} />

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

Nous pouvons utiliser cette astuce avec nous prenons donc en charge toute propriété arbitraire qui les soutiens. En faisant cela, nous nous prouvons également pour l'avenir; si ajoute de nouvelles propriétés à l'avenir, 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 le formulaire à fermeture automatique au lieu de référencer explicitement {props.children} entre le Mots clés. React permet children à passer comme accessoire régulier ou comme éléments enfants d'un composant entre les balises d'ouverture et de fermeture.

Classement 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 , puisque nous passons toutes les propriétés par:

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

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

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

Lorsque vous avez la même propriété plusieurs fois dans un composant React, le la dernière déclaration l'emporte. Cela signifie que notre dernier activeClassName="active-link" la déclaration 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 répartir this.props 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 en a vraiment besoin:

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

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

Créateurs de composants d'ordre supérieur

Souvent, vous aurez un certain nombre de composants dont vous aurez besoin pour intégrer le même comportement. Ceci est très similaire au début de cet article lorsque nous avons terminé add et subtract pour leur ajouter une journalisation.

Imaginons dans votre application que vous ayez un objet contenant des informations sur l’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 souhaitez être plus strict sur les composants qui reçoivent les informations.

La façon de résoudre ce problème est de créer une 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 cela est rendu plus simple avec du code:

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

  
  
  return function(props) {
    
    
    return <Component user={secretUserInfo} {...props} />
  }
}

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 retourne une nouvelle fonction qui rendra le composant qui lui a été donné avec une propriété supplémentaire de user, qui est réglé sur secretUserInfo.

Prenons maintenant un composant, , qui souhaite accéder à ces informations afin d'afficher l'utilisateur connecté:

const AppHeader = function(props) {
  if (props.user) {
    return <p>Logged in as {props.user.name}p>;
  } else {
    return <p>You need to loginp>;
  }
}

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 wrapWithUser une fonction:

const ConnectedAppHeader = wrapWithUser(AppHeader);

Nous avons maintenant un composant qui peut être rendu et aura accès au user objet.

J'ai choisi d'appeler le composant ConnectedAppHeader parce que je pense que c'est lié à 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 à React des principes de programmation fonctionnelle tels que des fonctions pures et des composants d'ordre supérieur, vous pouvez créer une base de code plus facile à maintenir 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 se développe, vous trouverez cela bénéfique.

Si vous avez des questions, je serais ravi de les entendre. N'hésitez pas à me cingler @Jack_Franklin sur Twitter.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *