Écrire du code propre est une compétence qui devient obligatoire à un certain stade de la carrière d'un programmeur. Cette compétence est particulièrement importante lorsque le programmeur essaie de trouver son premier emploi. C'est essentiellement ce qui fait du développeur un joueur d'équipe et quelque chose qui peut soit «remplir» l'entrevue, soit l'aider à réussir. Les employeurs, lorsqu'ils prennent des décisions en matière de personnel, consultent le code rédigé par leurs employés potentiels. Le code que le programmeur écrit doit être compris non seulement par les machines, mais aussi par les personnes.

Le matériel, dont nous publions aujourd'hui la première partie de la traduction, présente des conseils pour écrire du code propre pour les applications React. La pertinence de ces conseils est d'autant plus élevée que la taille du projet dans lequel les principes qui y sont énoncés sont appliqués est grande. Dans les petits projets, vous pouvez probablement vous passer de l'application de ces principes. Lorsque vous décidez de ce qui est nécessaire dans chaque situation particulière, il vaut la peine d'être guidé par le bon sens.
1. Propriétés de destruction
Les propriétés destructrices (dans la terminologie React en anglais, elles sont appelées «accessoires») sont un bon moyen de rendre le code plus propre et d'améliorer ses capacités de support. Le fait est que cela vous permet d'exprimer ou de déclarer clairement ce qu'une entité utilise (comme le composant React). Cependant, cette approche n'oblige pas les développeurs à lire dans l'implémentation du composant afin de connaître la composition des propriétés qui lui sont associées.
Les propriétés de destruction permettent également au programmeur de définir leurs valeurs par défaut. Ceci est assez courant:
import React from 'react' import Button from 'components/Button' const MyComponent = ({ placeholder = '', style, ...otherProps }) => { return ( <Button type="button" style={{ border: `1px solid ${placeholder ? 'salmon' : '#333'}`, ...style, }} {...otherProps} > Click Me </Button> ) } export default MyComponent
L'une des conséquences les plus agréables de l'utilisation de la déstructuration en JavaScript, que j'ai pu trouver, est qu'elle vous permet de prendre en charge diverses options de paramètres.
Par exemple, nous avons une fonction
authenticate
, qui a pris comme paramètre le
token
utilisé pour authentifier les utilisateurs. Plus tard, il a fallu lui faire accepter l'entité
jwt_token
. Ce besoin était dû à un changement dans la structure de la réponse du serveur. Grâce à l'utilisation de la déstructuration, vous pouvez facilement organiser la prise en charge des deux paramètres sans avoir à gérer la nécessité de modifier la plupart du code de fonction:
L'
jwt_token
sera évaluée lorsque le code atteindra le
token
. En conséquence, si
jwt_token
se révèle être un jeton valide et que l'entité de
token
s'avère
undefined
, la valeur de
jwt_token
tombera dans le
token
. Si dans le
token
il y avait déjà une valeur qui n'était pas fausse par les règles JS (c'est-à-dire un vrai jeton), alors dans le
token
aura simplement ce qui est déjà là.
2. Placer les fichiers de composants dans une structure de dossiers bien pensée
Jetez un œil à la structure de répertoires suivante:
- src
- composants
- Breadcrumb.js
- CollapsedSeparator.js
- Entrée
- index.js
- Input.js
- utils.js
- focusManager.js
- Carte
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typography.js
Le fil d'Ariane peut inclure des séparateurs. Le composant
CollapsedSeparator
est importé dans le fichier
Breadcrumb.js
. Cela nous permet de savoir que dans la mise en œuvre du projet en question, ils sont liés. Cependant, une personne qui ne possède pas ces informations peut suggérer que
Breadcrumb
et
CollapsedSeparator
sont une paire de composants complètement indépendants qui ne sont en aucun cas connectés les uns aux autres. Surtout - si
CollapsedSeparator
aucun signe clair que ce composant est associé au composant
Breadcrumb
. Parmi ces signes, par exemple, il peut y avoir un préfixe
Breadcrumb
utilisé dans le nom du composant, qui peut transformer le nom en quelque chose comme
BreadcrumbCollapsedSeparator.js
.
Comme nous savons que
Breadcrumb
et
CollapsedSeparator
sont liés l'un à l'autre, nous pouvons nous demander pourquoi ils ne sont pas placés dans un dossier séparé, comme
Input
et
Card
. Dans le même temps, nous pouvons commencer à faire diverses hypothèses sur la raison pour laquelle les matériaux du projet ont juste une telle structure. Disons, ici, vous pouvez penser à ce que ces composants ont été placés au niveau supérieur du projet afin de les aider à trouver rapidement ces composants, en prenant soin de ceux qui travailleront avec le projet. En conséquence, la relation entre les parties du projet semble plutôt vague pour le nouveau développeur. L'utilisation de techniques d'écriture de code propres devrait avoir exactement l'effet inverse. Le fait est que grâce à eux, le nouveau développeur a la possibilité de lire le code de quelqu'un d'autre et de saisir instantanément l'essence de la situation.
Si nous utilisons une structure de répertoires bien pensée dans notre exemple, nous obtenons quelque chose comme ceci:
- src
- Fil d'Ariane
- index.js
- Breadcrumb.js
- CollapsedSeparator.js
- Entrée
- index.js
- Input.js
- utils.js
- focusManager.js
- Carte
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typography.js
Maintenant, peu importe le nombre de composants associés au composant
Breadcrumb
qui seront créés. Tant que leurs fichiers se trouvent dans le même répertoire que
Breadcrumb.js
, nous saurons qu'ils sont associés au composant
Breadcrumb
:
- src
- Fil d'Ariane
- index.js
- Breadcrumb.js
- CollapsedSeparator.js
- Expander.js
- BreadcrumbText.js
- Breadcrumbhothotog.js
- Breadcrumbfishes.js
- Breadcrumbleftft.js
- Breadcrumbhead.js
- Breadcrumbaddict.js
- Breadcrumbdragon0814.js
- Breadcrumbcontext.js
- Entrée
- index.js
- Input.js
- utils.js
- focusManager.js
- Carte
- index.js
- Card.js
- CardDivider.js
- Button.js
- Typography.js
Voici à quoi ressemble le travail avec des structures similaires dans le code:
import React from 'react' import Breadcrumb, { CollapsedSeparator, Expander, BreadcrumbText, BreadcrumbHotdog, BreadcrumbFishes, BreadcrumbLeftOvers, BreadcrumbHead, BreadcrumbAddict, BreadcrumbDragon0814, } from '../../../../../../../../../../components/Breadcrumb' const withBreadcrumbHotdog = (WrappedComponent) => (props) => ( <WrappedComponent BreadcrumbHotdog={BreadcrumbHotdog} {...props} /> ) const WorldOfBreadcrumbs = ({ BreadcrumbHotdog: BreadcrumbHotdogComponent, }) => { const [hasFishes, setHasFishes] = React.useState(false) return ( <BreadcrumbDragon0814 hasFishes={hasFishes} render={(results) => ( <BreadcrumbFishes> {({ breadcrumbFishes }) => ( <BreadcrumbLeftOvers.Provider> <BreadcrumbHotdogComponent> <Expander> <BreadcrumbText> <BreadcrumbAddict> <pre> <code>{JSON.stringify(results, null, 2)}</code> </pre> </BreadcrumbAddict> </BreadcrumbText> </Expander> {hasFishes ? breadcrumbFishes.map((fish) => ( <> {fish} <CollapsedSeparator /> </> )) : null} </BreadcrumbHotdogComponent> </BreadcrumbLeftOvers.Provider> )} </BreadcrumbFishes> )} /> ) } export default withBreadcrumbHotdog(WorldOfBreadcrumbs)
3. Nommer les composants à l'aide des conventions de dénomination standard
L'utilisation de certaines normes lors de la dénomination des composants permet à une personne qui n'est pas l'auteur du projet de lire facilement le code de ce projet.
Par exemple, les noms des
composants d'ordre supérieur (HOC) sont généralement précédés du préfixe. De nombreux développeurs sont habitués à ces noms de composants:
import React from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' import getDisplayName from 'utils/getDisplayName' const withFreeMoney = (WrappedComponent) => { class WithFreeMoney extends React.Component { giveFreeMoney() { return 50000 } render() { return ( <WrappedComponent additionalMoney={[ this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), ]} {...this.props} /> ) } } WithFreeMoney.displayName = `withFreeMoney(${getDisplayName( WrappedComponent, )}$)` hoistNonReactStatics(WithFreeMoney, WrappedComponent) return WithFreeMoney } export default withFreeMoney
Supposons que quelqu'un décide de prendre du recul par rapport à cette pratique et procédez comme suit:
import React from 'react' import hoistNonReactStatics from 'hoist-non-react-statics' import getDisplayName from 'utils/getDisplayName' const useFreeMoney = (WrappedComponent) => { class WithFreeMoney extends React.Component { giveFreeMoney() { return 50000 } render() { return ( <WrappedComponent additionalMoney={[ this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), this.giveFreeMoney(), ]} {...this.props} /> ) } } WithFreeMoney.displayName = `useFreeMoney(${getDisplayName( WrappedComponent, )}$)` hoistNonReactStatics(WithFreeMoney, WrappedComponent) return WithFreeMoney } export default useFreeMoney
Il s'agit d'un code JavaScript parfaitement fonctionnel. Les noms ici sont composés, d'un point de vue technique, à droite. Mais le préfixe
use
est habituel à utiliser dans d'autres situations, notamment lors du nommage des
hooks React . Par conséquent, si quelqu'un écrit un programme qu'il envisage de montrer à quelqu'un d'autre, il doit faire attention aux noms des entités. Cela est particulièrement vrai dans les cas où quelqu'un demande à voir son code et à l'aider à résoudre un problème. Le fait est que quelqu'un qui lit le code de quelqu'un d'autre, très probablement, est déjà habitué à un certain schéma de dénomination d'entité.
Les écarts par rapport aux normes généralement acceptées rendent difficile la compréhension du code de quelqu'un d'autre.
4. Évitez les pièges booléens
Le programmeur doit faire preuve d'une extrême prudence dans le cas où certaines sorties dépendent de certaines valeurs logiques primitives, et certaines décisions sont prises en fonction de l'analyse de ces valeurs. Cela fait allusion à la mauvaise qualité du code. Cela oblige les développeurs à lire le code d'implémentation de composants ou d'autres mécanismes pour avoir une idée précise du résultat exact de ces mécanismes.
Supposons que nous avons créé un composant
Typography
qui peut accepter les options suivantes:
'h1'
,
'h2'
,
'h3'
,
'h4'
,
'h5
',
'h6'
,
'title'
,
'subheading'
.
Qu'est-ce qui affectera exactement la sortie d'un composant si les options lui sont transmises sous la forme suivante?
const App = () => ( <Typography color="primary" align="center" subheading title> Welcome to my bio </Typography> )
Ceux qui ont une certaine expérience avec React (ou plutôt avec JavaScript) peuvent déjà supposer que l'option
title
subheading
option de
subheading
raison du fonctionnement du système. La dernière option remplacera la première.
Mais le problème ici est que nous ne pouvons pas, sans regarder dans le code, dire exactement dans quelle mesure l'option de
title
ou l'option de
subheading
-
subheading
sera appliquée.
Par exemple:
.title { font-size: 1.2rem; font-weight: 500; text-transform: uppercase; } .subheading { font-size: 1.1rem; font-weight: 400; text-transform: none !important; }
Même si le
title
emporte, la règle CSS
text-transform: uppercase
ne s'applique pas. Cela est dû à la spécificité plus élevée de la
text-transform: none !important
Règle
text-transform: none !important
qui existe dans le
subheading
. Si vous ne faites pas preuve de prudence dans de telles situations, le débogage de telles erreurs dans les styles peut devenir extrêmement difficile. Surtout - dans les cas où le code n'affiche pas certains avertissements ou messages d'erreur dans la console. Cela peut compliquer la signature du composant.
Une solution possible à ce problème consiste à utiliser une version plus propre du composant
Typography
:
const App = () => <Typography variant="title">Welcome to my bio</Typography>
Voici le code du composant
Typography
:
import React from 'react' import cx from 'classnames' import styles from './styles.css' const Typography = ({ children, color = '#333', align = 'left', variant, ...otherProps }) => { return ( <div className={cx({ [styles.h1]: variant === 'h1', [styles.h2]: variant === 'h2', [styles.h3]: variant === 'h3', [styles.h4]: variant === 'h4', [styles.h5]: variant === 'h5', [styles.h6]: variant === 'h6', [styles.title]: variant === 'title', [styles.subheading]: variant === 'subheading', })} > {children} </div> ) }
Maintenant, lorsque dans le composant
App
, nous passons au composant
Typography
variant="title"
, nous pouvons être sûrs que seul le
title
affectera la sortie du composant. Cela nous évite d'avoir à analyser le code du composant afin de comprendre à quoi ressemblera ce composant.
Pour travailler avec des propriétés, vous pouvez utiliser la construction simple
if/else
:
let result if (variant === 'h1') result = styles.h1 else if (variant === 'h2') result = styles.h2 else if (variant === 'h3') result = styles.h3 else if (variant === 'h4') result = styles.h4 else if (variant === 'h5') result = styles.h5 else if (variant === 'h6') result = styles.h6 else if (variant === 'title') result = styles.title else if (variant === 'subheading') result = styles.subheading
Mais la principale force de cette approche est que vous pouvez simplement utiliser le design simple ligne suivant et y mettre fin:
const result = styles[variant]
5. Utilisez les fonctions fléchées
Les fonctions fléchées représentent un mécanisme concis et clair pour déclarer des fonctions en JavaScript (dans ce cas, il serait plus correct de parler de l'avantage des fonctions fléchées par rapport aux expressions fonctionnelles).
Cependant, dans certains cas, les développeurs n'utilisent pas de fonctions fléchées au lieu d'expressions fonctionnelles. Par exemple, quand il est nécessaire d'organiser l'augmentation des fonctions.
React utilise ces concepts de manière similaire. Cependant, si un programmeur n'est pas intéressé à augmenter les fonctions, alors, à mon avis, il est logique d'utiliser la syntaxe des fonctions fléchées:
Il convient de noter qu'en analysant cet exemple, il est difficile de voir les forces des fonctions fléchées. Leur beauté se manifeste pleinement lorsqu'il s'agit de conceptions simples à une seule ligne:
Je suis sûr que ces conceptions unifilaires plairont à tout le monde.
6. Placez des fonctions indépendantes en dehors de vos propres crochets
J'ai vu comment certains programmeurs déclarent des fonctions dans leurs propres hooks, mais ces hooks n'ont pas particulièrement besoin de telles fonctions. Ce type "gonfle" légèrement le code du crochet et complique sa lecture. Des difficultés de lecture du code surviennent du fait que ses lecteurs peuvent commencer à se demander si le crochet dépend vraiment de la fonction qui s'y trouve. Si ce n'est pas le cas, il vaut mieux déplacer la fonction hors du crochet. Cela donnera au lecteur de code une compréhension claire de ce dont dépend le crochet et de ce qu'il ne fait pas.
Voici un exemple:
import React from 'react' const initialState = { initiated: false, images: [], } const reducer = (state, action) => { switch (action.type) { case 'initiated': return { ...state, initiated: true } case 'set-images': return { ...state, images: action.images } default: return state } } const usePhotosList = ({ imagesList = [] }) => { const [state, dispatch] = React.useReducer(reducer, initialState) const removeFalseyImages = (images = []) => images.reduce((acc, img) => (img ? [...acc, img] : acc), []) React.useEffect(() => { const images = removeFalseyImages(imagesList) dispatch({ type: 'initiated' }) dispatch({ type: 'set-images', images }) }, []) return { ...state, } } export default usePhotosList
Si nous analysons ce code, nous pouvons comprendre que les fonctions
removeFalseyImages
, en fait, n'ont pas besoin d'être présentes à l'intérieur du crochet; il n'interagit pas avec son état, ce qui signifie qu'il peut être placé à l'extérieur et peut être appelé à partir du crochet sans aucun problème.
7. Soyez cohérent lorsque vous écrivez du code
Une approche cohérente pour écrire du code est quelque chose qui est souvent recommandé pour ceux qui programment en JavaScript.
Dans le cas de React, il convient de prêter attention à une approche cohérente de l'utilisation des conceptions suivantes:
- Équipes d'importation et d'exportation.
- Dénomination des composants, des crochets, des composants d'ordre supérieur, des classes.
Lors de l'importation et de l'exportation de composants, j'utilise parfois quelque chose de similaire au suivant:
import App from './App' export { default as Breadcrumb } from './Breadcrumb' export default App
Mais j'aime aussi la syntaxe:
export { default } from './App' export { default as Breadcrumb } from './Breadcrumb'
Quoi que le programmeur choisisse, il doit l'utiliser de manière cohérente dans chaque projet qu'il crée. Cela simplifie le travail de ce programmeur et la lecture de son code par d'autres personnes.
Il est très important de respecter les conventions de dénomination des entités.
Par exemple, si quelqu'un a donné au hook le nom
useApp
, il est important que les noms des autres hooks soient construits de la même manière - en utilisant le préfixe use. Par exemple, le nom d'un autre hook avec cette approche peut ressembler à
useController
.
Si vous n'adhérez pas à cette règle, le code d'un projet peut finalement se révéler être quelque chose comme ceci:
Voici à quoi ressemble l'importation de ces crochets:
import React from 'react' import useApp from './useApp' import basicController from './basicController' const App = () => { const app = useApp() const controller = basicController() return ( <div> {controller.errors.map((errorMsg) => ( <div>{errorMsg}</div> ))} </div> ) } export default App
À première vue, il est tout à fait évident que le
basicController
est un crochet, le même que
useApp
. Cela oblige le développeur à lire le code d'implémentation de ce qu'il importe. Cela n'est fait que pour comprendre exactement ce à quoi le développeur doit faire face. Si nous adhérons systématiquement à la même stratégie pour nommer les entités, une telle situation ne se produira pas. Tout sera clair en un coup d'œil:
const app = useApp() const controller = useBasicController()
À suivre ...
Chers lecteurs! Comment abordez-vous le nommage d'entités dans vos projets React?
