11 conseils pour utiliser Redux lors du développement d'applications React

Lorsqu'il s'agit de développer des applications React, en termes d'architecture de code, les petits projets sont souvent plus flexibles que les grands. Il n'y a rien de mal à créer de tels projets en utilisant des directives pratiques destinées à des applications plus importantes. Mais tout cela, dans le cas de petits projets, peut être tout simplement inutile. Plus l'application est petite, plus elle est «condescendante» et fait référence à l'utilisation de solutions simples, éventuellement non optimales, mais ne nécessitant pas beaucoup de temps pour leur mise en œuvre.



Malgré cela, je voudrais noter que certaines des recommandations qui seront données dans ce document visent les applications React de n'importe quelle échelle.

Si vous n'avez jamais créé d'application de production, cet article peut vous aider à vous préparer au développement de solutions à grande échelle. Quelque chose comme ça pourrait très bien devenir l'un de vos prochains projets. La pire chose qui puisse arriver à un programmeur, c'est quand il travaille sur un projet et se rend compte qu'il a besoin de refactoriser de grandes quantités de code pour améliorer l'évolutivité et la maintenabilité de l'application. Tout semble encore pire s'il n'y avait pas de tests unitaires dans le projet avant refactoring.

L'auteur de ce document demande au lecteur de croire sur parole. Il a vécu des situations similaires. Ainsi, il a obtenu plusieurs tâches qui devaient être résolues dans un certain temps. Au début, il pensait que tout ce qu'il faisait était excellent. La source de ces réflexions était que son application Web, après avoir apporté des modifications, a continué à fonctionner, et en même temps a continué à fonctionner rapidement. Il savait comment utiliser Redux, comment établir une interaction normale entre les composants de l'interface utilisateur. Il lui semblait qu'il comprenait profondément les concepts de réducteurs et d'actions. Il se sentait invulnérable.

Mais ici, l'avenir s'est glissé.

Après quelques mois de travail sur l'application, plus de 15 nouvelles fonctionnalités y ont été ajoutées. Après cela, le projet est devenu incontrôlable. Le code qui utilisait la bibliothèque Redux est devenu très difficile à maintenir. Pourquoi est-ce arrivé? Au début, ne semblait-il pas que le projet attendait une vie longue et sans nuage?

L'auteur de l'article dit qu'en posant des questions similaires, il s'est rendu compte qu'il avait posé de sa propre main une bombe à retardement dans le projet.

La bibliothèque Redux, si elle est utilisée correctement dans les grands projets, aide, à mesure que ces projets se développent, à garder leur code dans un état pris en charge.

Voici 11 conseils pour ceux qui souhaitent développer des applications React évolutives à l'aide de Redux.

1. Ne placez pas le code d'action et les constantes au même endroit


Vous pourriez rencontrer des didacticiels Redux dans lesquels les constantes et toutes les actions sont placées au même endroit. Cependant, cette approche, à mesure que l'application se développe, peut rapidement entraîner des problèmes. Les constantes doivent être stockées séparément, par exemple dans ./src/constants . Par conséquent, pour rechercher des constantes, vous devez regarder un seul dossier, et non plusieurs.

De plus, la création de fichiers séparés stockant des actions semble tout à fait normale. Ces fichiers encapsulent des actions directement liées les unes aux autres. Les actions dans un seul fichier, par exemple, peuvent avoir des similitudes en termes de quoi et comment elles sont utilisées.

Supposons que vous développez un jeu d'arcade ou un jeu de rôle et que vous créez les classes warrior (guerrier), sorceress (sorcière) et archer (archer). Dans une telle situation, vous pouvez atteindre un niveau élevé de prise en charge de code en organisant les actions comme suit:

 src/actions/warrior.js src/actions/sorceress.js src/actions/archer.js 

Ce sera bien pire si tout tombe dans un seul fichier:

 src/actions/classes.js 

Si l'application devient très volumineuse, il pourrait être encore mieux d'utiliser approximativement la structure suivante de fractionnement de code en fichiers:

 src/actions/warrior/skills.js src/actions/sorceress/skills.js src/actions/archer/skills.js 

Seul un petit fragment d'une telle structure est montré ici. Si vous envisagez cette approche de manière plus large et cohérente, vous vous retrouverez avec quelque chose comme cet ensemble de fichiers:

 src/actions/warrior/skills.js src/actions/warrior/quests.js src/actions/warrior/equipping.js src/actions/sorceress/skills.js src/actions/sorceress/quests.js src/actions/sorceress/equipping.js src/actions/archer/skills.js src/actions/archer/quests.js src/actions/archer/equipping.js 

Voici à quoi pourrait ressembler l'action du fichier src/actions/sorceress/skills pour l'objet sorceress:

 import { CAST_FIRE_TORNADO, CAST_LIGHTNING_BOLT } from '../constants/sorceress' export const castFireTornado = (target) => ({ type: CAST_FIRE_TORNADO, target, }) export const castLightningBolt = (target) => ({ type: CAST_LIGHTNING_BOLT, target, }) 

Voici le contenu du src/actions/sorceress/equipping :

 import * as consts from '../constants/sorceress' export const equipStaff = (staff, enhancements) => {...} export const removeStaff = (staff) => {...} export const upgradeStaff = (slot, enhancements) => { return (dispatch, getState, { api }) => {   //                 const state = getState()   const currentEquipment = state.classes.sorceress.equipment.current   const staff = currentEquipment[slot]   const isMax = staff.level >= 9   if (isMax) {     return   }   dispatch({ type: consts.UPGRADING_STAFF, slot })   api.upgradeEquipment({     type: 'staff',     id: currentEquipment.id,     enhancements,   })   .then((newStaff) => {     dispatch({ type: consts.UPGRADED_STAFF, slot, staff: newStaff })   })   .catch((error) => {     dispatch({ type: consts.UPGRADE_STAFF_FAILED, error })   }) } } 

La raison pour laquelle nous organisons le code de cette manière est que de nouvelles fonctionnalités sont constamment ajoutées aux projets. Cela signifie que nous devons être préparés à leur apparence et en même temps nous efforcer de veiller à ce que les fichiers ne soient pas surchargés de code.

Au tout début des travaux sur le projet, cela peut sembler inutile. Mais plus le projet prend de l'ampleur, plus la force d'une telle approche se fera sentir.

2. Ne placez pas le code réducteur au même endroit


Quand je vois que le code de mes réducteurs se transforme en quelque chose de similaire à celui illustré ci-dessous, je comprends que je dois changer quelque chose.

 const equipmentReducers = (state, action) => { switch (action.type) {   case consts.UPGRADING_STAFF:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: action.slot,           },         },       },     }   case consts.UPGRADED_STAFF:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: null,             current: {               ...state.classes.sorceress.equipment.current,               [action.slot]: action.staff,             },           },         },       },     }   case consts.UPGRADE_STAFF_FAILED:     return {       ...state,       classes: {         ...state.classes,         sorceress: {           ...state.classes.sorceress,           equipment: {             ...state.classes.sorceress.equipment,             isUpgrading: null,           },         },       },     }   default:     return state } } 

Un tel code, sans aucun doute, pourrait très rapidement conduire à beaucoup de dégâts. Par conséquent, il est préférable de maintenir la structure du travail avec l'État sous la forme la plus simple possible, en visant le niveau minimum de leur imbrication. Vous pouvez, au contraire, essayer de recourir à la composition des réducteurs.

Une astuce utile pour travailler avec des réducteurs consiste à créer un réducteur d'ordre supérieur généré par d'autres réducteurs. En savoir plus ici.

3. Utilisez des noms de variables informatifs


Nommer des variables, à première vue, peut sembler une tâche élémentaire. Mais en fait, cette tâche peut être l'une des plus difficiles.

La sélection des noms de variables est généralement pertinente pour les directives pratiques pour l'écriture de code propre. La raison pour laquelle il existe en général un «nom de variable» est que cet aspect du développement du code joue un rôle très important dans la pratique. Une sélection infructueuse des noms de variables est un moyen sûr de vous nuire à vous-même et aux membres de votre équipe à l'avenir.

Avez-vous déjà essayé de modifier le code de quelqu'un d'autre et en même temps rencontré des difficultés à comprendre ce que fait exactement ce code? Avez-vous déjà exécuté un programme étranger et constaté qu'il ne fonctionne pas comme prévu?

Je dirais que dans de tels cas, vous avez rencontré le soi-disant "code sale".

Si vous devez gérer un code similaire dans de grandes applications, ce n'est qu'un cauchemar. Malheureusement, cela se produit assez souvent.

Voici un cas de la vie. J'ai édité le code du crochet React à partir d'une application et à ce moment, ils m'ont envoyé une tâche. Il s'agissait de mettre en œuvre dans l'application la possibilité d'afficher des informations supplémentaires sur les médecins. Cette information aurait dû être montrée au patient qui clique sur la photo de profil du médecin. Il fallait le retirer de la table, il devait arriver au client après avoir traité la prochaine requête au serveur.

Cette tâche n'était pas difficile, le principal problème que j'ai rencontré était que je devais passer trop de temps à trouver exactement où ce dont j'avais besoin se trouvait dans le code du projet.

J'ai recherché dans le code les mots info , dataToSend , dataObject et d'autres qui, à mon dataObject , sont associés aux données reçues du serveur. Après 5 à 10 minutes, j'ai réussi à trouver le code responsable du travail avec les données dont j'avais besoin. L'objet dans lequel ils se sont retrouvés s'appelait paymentObject . À mon avis, un objet lié aux paiements peut contenir quelque chose comme un code CVV, un numéro de carte de crédit, un code postal de payeur et d'autres informations similaires. L'objet que j'ai découvert avait 11 propriétés. Seuls trois d'entre eux étaient liés aux paiements: le mode de paiement, l'identifiant du profil de paiement et une liste de codes de coupon.

La situation ne s'est pas améliorée non plus car j'ai dû apporter à cet objet les modifications nécessaires à la résolution de la tâche qui m'attendait.

En bref, il est recommandé de ne pas utiliser de noms obscurs pour les fonctions et les variables. Voici un exemple de code dans lequel le nom de la fonction notify révèle pas sa signification:

 import React from 'react' class App extends React.Component { state = { data: null } //  -? notify = () => {   if (this.props.user.loaded) {     if (this.props.user.profileIsReady) {       toast.alert(         'You are not approved. Please come back in 15 minutes or you will be deleted.',         {           position: 'bottom-right',           timeout: 15000,         },       )     }   } } render() {   return this.props.render({     ...this.state,     notify: this.notify,   }) } } export default App 

4. Ne modifiez pas les structures ou les types de données dans les flux de données d'application déjà configurés


L'une des plus grandes erreurs que j'ai jamais commises a été de modifier la structure des données dans un flux de données d'application déjà configuré. La nouvelle structure de données apporterait une énorme amélioration des performances, car elle utilisait des méthodes rapides pour rechercher des données dans des objets stockés en mémoire, au lieu d'itérer sur des tableaux. Mais c'était trop tard.

Je vous demande de ne pas faire ça. Peut-être que quelque chose comme ça ne peut être offert qu'à quelqu'un qui sait exactement quelles parties de l'application cela peut affecter.

Quelles sont les conséquences d'une telle démarche? Par exemple, si quelque chose était d'abord un tableau, puis devenait un objet, cela peut perturber le fonctionnement de nombreuses parties de l'application. J'ai fait une énorme erreur en croyant que je pouvais me souvenir de tous les endroits du code qui pourraient être affectés par un changement dans la présentation des données structurées. Cependant, dans de tels cas, il y a toujours un morceau de code qui est affecté par le changement, et dont personne ne se souvient.

5. Utilisez des extraits


J'étais un fan de l'éditeur Atom, mais je suis passé à VS Code car cet éditeur était incroyablement rapide par rapport à Atom. Et lui, à sa vitesse, prend en charge un grand nombre de possibilités différentes.

Si vous utilisez également VS Code, je vous recommande d'installer l' extension Project Snippets . Cette extension permet au programmeur de créer des extraits personnalisés pour chaque espace de travail utilisé dans un projet. Cette extension fonctionne de la même manière que le mécanisme Use Snippets intégré dans VS Code. La différence est que lorsque vous travaillez avec des extraits de projet, le .vscode/snippets/ est créé dans le projet. Il ressemble à la figure suivante.


Le contenu du dossier .vscode / snippets /

6. Créez des tests unitaires, de bout en bout et d'intégration


Au fur et à mesure que la taille de l'application augmente, il devient plus effrayant pour le programmeur de modifier du code qui n'est pas couvert par les tests. Par exemple, il peut arriver que quelqu'un modifie le code stocké dans src/x/y/z/ et décide de l'envoyer en production. Si en même temps les modifications apportées affectent les parties du projet auxquelles le programmeur n'a pas pensé, alors tout peut se terminer par une erreur qu'un véritable utilisateur rencontrera. S'il y a des tests dans le projet, le programmeur sera informé de l'erreur bien avant que le code ne soit mis en production.

7. Remue-méninges


Les programmeurs, en train d'introduire de nouvelles fonctionnalités dans les projets, refusent souvent de réfléchir. Cela se produit car une telle activité n'est pas liée à l'écriture de code. Cela se produit particulièrement souvent lorsque très peu de temps est alloué à la tâche.

Et pourquoi, au fait, devez-vous faire un brainstorming lors du développement d'applications?

Le fait est que plus l'application devient complexe, plus les programmeurs doivent porter attention à ses différentes parties. Le brainstorming permet de réduire le temps nécessaire pour refactoriser le code. Après leur détention, le programmeur est armé de la connaissance de ce qui peut mal se passer pendant l'achèvement du projet. Souvent, les programmeurs, lorsqu'ils développent une application, ne prennent même pas la peine de réfléchir au moins un peu à la façon de tout faire de la meilleure façon.

C'est pourquoi le brainstorming est très important. Au cours d'un tel événement, le programmeur peut considérer l'architecture du code, réfléchir à la façon d'apporter les modifications nécessaires au programme, retracer le cycle de vie de ces changements et créer une stratégie pour travailler avec eux. Cela ne vaut pas la peine de prendre l'habitude de garder tous les plans exclusivement dans votre tête. C'est ce que font les programmeurs qui sont trop confiants. Mais se souvenir absolument de tout est tout simplement impossible. Et, dès que quelque chose ne va pas, des problèmes apparaissent l'un après l'autre. C'est le principe des dominos en action.

Le brainstorming est également utile en équipe. Par exemple, si au cours d'un travail, quelqu'un rencontre un problème, il peut se tourner vers le matériel de la session de remue-méninges, car le problème qui s'est posé avec lui aurait peut-être déjà été envisagé. Les notes prises pendant la séance de remue-méninges pourraient bien jouer le rôle d'un plan pour résoudre le problème. Ce plan vous permet d'évaluer clairement la quantité de travail effectuée.

8. Créer des maquettes d'application


Si vous souhaitez commencer à développer l'application, vous devez prendre une décision sur son apparence et la façon dont les utilisateurs interagiront avec. Cela signifie que vous devrez créer une disposition d'application. Vous pouvez utiliser différents outils pour cela.

Moqups est l'un des outils de maquette d'application dont j'entends souvent parler. Il s'agit d'un outil rapide créé en utilisant HTML5 et JavaScript et n'impose pas d'exigences particulières au système.

La création d'une application fictive simplifie et accélère considérablement le processus de développement. La mise en page donne au développeur des informations sur la relation entre les différentes parties de l'application et sur le type de données qui seront affichées sur ses pages.

9. Planifier le flux de données dans les applications


Presque tous les composants de votre application seront associés à certaines données. Certains composants utilisent leurs propres sources de données, mais la plupart des composants reçoivent des données d'entités situées au-dessus d'eux dans la hiérarchie des composants. Pour les parties de l'application dans lesquelles les mêmes données sont partagées par plusieurs composants, il est utile de fournir un stockage d'informations centralisé situé au niveau supérieur de la hiérarchie. C'est dans de telles situations que la bibliothèque Redux peut apporter une aide précieuse au développeur.

Je recommande que pendant que vous travaillez sur l'application, établissez un diagramme montrant la façon dont les données se déplacent dans cette application. Cela aidera à créer un modèle d'application clair, de plus, nous parlons du code et de la perception de l'application par le programmeur. Un tel modèle contribuera en outre à la création de réducteurs.

10. Utiliser les fonctionnalités d'accès aux données


À mesure que la taille de l'application augmente, le nombre de ses composants augmente également. Et lorsque le nombre de composants augmente, la même chose se produit avec la fréquence d'utilisation des sélecteurs (react-redux ^ v7.1) ou mapStateToProps . Supposons que vous constatiez que vos composants ou hooks accèdent souvent à des fragments d'état dans différentes parties de l'application à l'aide d'une construction telle que useSelector((state) => state.app.user.profile.demographics.languages.main) . Si c'est le cas, cela signifie que vous devez penser à créer des fonctions d'accès aux données. Les fichiers dotés de telles fonctions doivent être stockés dans un lieu public à partir duquel les composants et les crochets peuvent les importer. Des fonctions similaires peuvent être des filtres, des analyseurs ou toute autre fonction de transformation de données.

Voici quelques exemples.

Par exemple, src/accessors peut contenir le code suivant:

 export const getMainLanguages = (state) => state.app.user.profile.demographics.languages.main 

Voici la version utilisant connect , qui peut être localisée le long du chemin src/components/ViewUserLanguages :

 import React from 'react' import { connect } from 'react-redux' import { getMainLanguages } from '../accessors' const ViewUserLanguages = ({ mainLanguages }) => ( <div>   <h1>Good Morning.</h1>   <small>Here are your main languages:</small>   <hr />   {mainLanguages.map((lang) => (     <div>{lang}</div>   ))} </div> ) export default connect((state) => ({ mainLanguages: getMainLanguages(state), }))(ViewUserLanguages) 

Voici la version qui utilise useSelector située dans src/components/ViewUserLanguages :

 import React from 'react' import { useSelector } from 'react-redux' import { getMainLanguages } from '../accessors' const ViewUserLanguages = ({ mainLanguages }) => { const mainLanguages = useSelector(getMainLanguages) return (   <div>     <h1>Good Morning.</h1>     <small>Here are your main languages:</small>     <hr />     {mainLanguages.map((lang) => (       <div>{lang}</div>     ))}   </div> ) } export default ViewUserLanguages 

De plus, veillez à ce que ces fonctions soient immuables, sans effets secondaires. Découvrez pourquoi je donne une telle recommandation ici .

11. Contrôlez le flux de données dans les propriétés à l'aide de la syntaxe de déstructuration et d'étalement


Quels sont les avantages de l'utilisation de la construction props.something rapport à la construction something ?

Voici à quoi cela ressemble sans utiliser la déstructuration:

 const Display = (props) => <div>{props.something}</div> 

Voici la même chose, mais avec l'utilisation de la déstructuration:

 const Display = ({ something }) => <div>{something}</div> 

L'utilisation de la déstructuration améliore la lisibilité du code. Mais cela ne limite pas son impact positif sur le projet. En utilisant la déstructuration, le programmeur est obligé de prendre des décisions sur ce que le composant reçoit exactement et ce qu'il sort exactement. Cela évite à quiconque doit modifier le code de quelqu'un d'autre d'avoir à regarder chaque ligne de la méthode de render à la recherche de toutes les propriétés utilisées par le composant.

En outre, cette approche offre une opportunité utile de définir des valeurs de propriété par défaut. Cela se fait au tout début du code du composant et élimine la nécessité d'écrire du code supplémentaire dans le corps du composant:

 const Display = ({ something = 'apple' }) => <div>{something}</div> 

Vous avez peut-être déjà vu quelque chose comme l'exemple suivant:

 const Display = (props) => ( <Agenda {...props}>   {' '}   //     Agenda   <h2><font color="#3AC1EF">Today is {props.date}</font></h2>   <hr />   <div>     <h3><font color="#3AC1EF">▍Here your list of todos:</font></h3>     {props.children}   </div> </Agenda> ) 

De telles constructions ne sont pas faciles à lire, mais ce n'est pas leur seul problème. Il y a donc une erreur. Si l'application affiche également des composants enfants, props.children s'affiche deux fois à l'écran. Si le travail sur un projet est effectué en équipe et que les membres de l'équipe ne sont pas assez prudents, la probabilité de telles erreurs est assez élevée.

Si vous détruisez des propriétés à la place, le code du composant sera plus clair et la probabilité d'erreurs diminuera:

 const Display = ({ children, date, ...props }) => ( <Agenda {...props}>   {' '}   //     Agenda   <h2><font color="#3AC1EF">Today is {date}</font></h2>   <hr />   <div>     <h3><font color="#3AC1EF">▍Here your list of todos:</font></h3>     {children}   </div> </Agenda> ) 

Résumé


Dans cet article, nous avons examiné 12 recommandations pour ceux qui développent des applications React à l'aide de Redux. Nous espérons que vous trouverez ici quelque chose qui vous sera utile.

Chers lecteurs! Quels conseils ajouteriez-vous à ceux de cet article?



Source: https://habr.com/ru/post/fr456336/


All Articles