React.js #1 : Comment architecturer son projet React.js ?

Cet article est le premier article d'une série de trois articles dans laquelle nous voyons comment bien débuter son projet React.

Pour ce premier article nous nous concentrerons sur comment organiser ses fichiers au sein d'un projet React. C'est parti ! 😉

Ce que dit React à propos de l'architecture projet

React doesn’t have opinions on how you put files into folders.
— Documentation officielle de React.js

React laisse la liberté sur la façon d’architecturer votre projet. Cependant, il présente deux approches populaires :

  • grouper les fichiers par fonctionnalité / route.
  • grouper les fichiers par type.

La structure du projet que nous allons présenter correspond à un regroupement par type de fichiers.

Pourquoi adopter une structure de projet ?

Pour ne pas avoir à faire ceci. Mais surtout pour éviter que lorsque votre projet grossit, cela devienne un gros bazar à cause d’une base mal posée / non posée.

building_collapsing_gif

Structurer son projet permet donc d’organiser ses fichiers selon une logique qui est définie et claire pour toute votre équipe. Cela permettra de faire grossir sereinement l’application avec de plus en plus de fichiers sans que cela devienne le bazar et que le fait de retrouver un bout de code devienne un calvaire.

Le problème que l’on rencontre avec React, c’est qu’à défaut de trouver une structure globalement acceptée par la communauté, on ne peut anticiper les problématiques futures, et c’est à ce moment que l’on peut regretter le choix de React.

Dans la structure d’un projet, il convient aussi d’aborder le nommage des fichiers qui est tout aussi important car il permet de s’y retrouver lorsqu’on a plusieurs fichiers ouverts dans notre éditeur de texte préféré.

On évitera donc de nommer nos fichiers avec des noms génériques comme index.js ou styles.css. On préférera dans le cas d’un composant bouton Button.js et Button.css.

Quelle structure faut-il choisir ?

La structure parfaite n’existe pas. C’est l’avantage mais aussi l’inconvénient de React car il laisse le choix sur la façon de faire. Il faudra donc choisir celle qui vous convient le mieux à vous et votre équipe en sachant que le but d’une structure de projet est simplement de rendre la navigation dans le code simple, rapide et scalable. Il faut donc choisir l’architecture qui vous permettra de perdre le moins de temps possible pour retrouver une partie de votre code que vous devez corriger ou améliorer.

La structure que nous avons choisie

Nous avons choisi une architecture composée de trois dossiers principaux : components, scenes et services. Décrivons-les un à un dans la suite de cet article.

files_tree_structure

Dans le dossier components, il y aura tous les composants réutilisables de l’application. Par exemple, dans notre application React, nous avons un composant Menu qui est utilisé dans d’autres fichiers de notre projet.

files_tree_structure_components_directory

Dans le dossier components on crée un dossier pour Menu dans lequel il y a le fichier javascript mais on pourrait ne pas le mettre dans un dossier dans le cas où on a un seul fichier dedans. Mais si nous souhaitons ajouter par la suite d’autres fichiers il faudra les regrouper dans un dossier, par anticipation nous créons toujours un dossier pour nos composants. En effet, on pourra y ajouter un fichier Menu.css pour le style ou encore Menu.test.js pour les tests unitaires.

Imaginons à présent que notre composant Menu soit composé de plusieurs composants MenuItem qui ne sont pas réutilisables dans toute l’application mais seulement dans le composant Menu. Comment faire cela ? Eh bien dans notre structure, nous pouvons reprendre les trois dossiers de base (components, scenes, services) à l’intérieur du dossier d’un composant. Voilà à quoi cela ressemblerait dans notre application :

files_tree_structure_components_directory_advanced

Ainsi la règle dans notre structure est qu’un composant à la racine du dossier components est réutilisable dans toute l’application mais les composants enfants d’un composant (MenuItem) sont utilisables seulement par le composant parent (Menu).

Dans le dossier scenes nous avons des assemblages de composants (dans certains frameworks, on appellerait cela views), nous y mettons les vues c’est-à-dire un ensemble d’éléments (balises JSX, composants réutilisables, …) qui forment une page. Une scène est généralement accessible par une unique route.

Comme précédemment, il est possible que ces scènes aient besoin d’un composant qui sera non réutilisable, dans ce cas il peut vivre dans un dossier components à l’intérieur d’une scène. Comme, dans l’exemple suivant, où notre scène Index, qui correspond à la page d’accueil, est la seule à avoir une barre latérale :

files_tree_structure_scenes_directory)

Dans le dossier services, on définit tous les services de notre application comme api, security, stores

files_tree_structure_services_directory)

Nous aurions également pu y mettre notre fichier de configuration des routes mais nous avons préféré le mettre à la racine du dossier src car il s’agit d’un élément central dans notre application et il sera plus aisé de le positionner à la racine de notre projet.

Encore une fois, vous êtes libres de structurer votre projet comme vous le souhaitez, ce qui compte c’est que vous soyez efficaces avec cette organisation. 👷‍♂️

Le nommage des fichiers

Name your file the same as the thing you’re exporting from that file.

Nommez votre composant en ayant à l’esprit ce qu’il représente.

Comme dit précédemment, il faut donner un nom explicite à vos fichiers et éviter les noms génériques comme index.js, car lorsque vous ouvrez plusieurs fichiers avec le même nom dans votre IDE, retrouver le bon fichier est vraiment galère ! Dans un premier temps, nommer un fichier de la même manière que son dossier parent peut être déroutant mais cette convention se comprend facilement. Vous pouvez retrouver l’article sur comment nommer ses fichiers.

Concernant la question de l’extension des fichiers de vos composants, vous pouvez utiliser .jsx ou .js. Dans notre cas nous avons choisi de mettre tous nos fichiers avec l’extension .js et non .jsx, de sorte à s’accorder avec la tendance de la communauté. Libre à vous de faire comme vous préférez mais Facebook semble suggérer d’utiliser .js pour l’extension de vos fichiers.

Une très bonne discussion à ce sujet est disponible pour vous faire votre propre avis. 🙃

Simplifer les imports : le problème des chemins relatifs

Afin d’éviter les imports relatifs avec les ../../../components/Button/Button qui peuvent être laborieux à écrire, nous sommes passés par les alias avec Webpack.

Problème : Comment faire pour modifier la configuration de Webpack en passant par create-react-app pour créer son application React ? Deux options :

  1. Une option est d’éjecter l’application avec un $ yarn run eject. Clairement, nous ne choisirons pas cette option car il deviendra par la suite plus fastidieux de gérer les mises à jour de React.

  2. L’autre option est de passer par la librairie react-app-rewired qui permet de modifier les scripts start, build et test pour utiliser une configuration webpack personnalisée. Il suffit pour cela d’installer la librairie :

    $ yarn add react-app-rewired --dev

Ensuite il suffit de créer un fichier config-overrides.js à la racine du projet. Voici le contenu de ce fichier dans notre cas :

const path = require('path');

module.exports = function override(config, env) {
    config["resolve"] = {
        alias: {
            components: path.resolve(__dirname, 'src/components/'),
            scenes: path.resolve(__dirname, 'src/scenes/'),
            services: path.resolve(__dirname, 'src/services/'),
            stores: path.resolve(__dirname, 'src/services/stores'),
            security: path.resolve(__dirname, 'src/services/security'),
            routes: path.resolve(__dirname, 'src/routes'),
        },
        extensions: ['.js']
    }

    return config;
}

Il faut ensuite remplacer les scripts start, build et test dans le fichier package.json. Il faut remplacer react-scripts par react-app-rewired, ce qui permet d’utiliser notre fichier de surcharge de configuration de webpack au démarrage, au test et à la construction de notre projet. Dans notre cas, nous avons créé six alias : components, scenes, services, stores, security, routes.

Ce qui permet d’importer nos fichiers d’une façon beaucoup plus épurée :

import { hasRoles } from 'services/security/hasRoles';
import Unauthorized from 'scenes/Unauthorized/Unauthorized';
import { getPath } from 'routes';

Merci d'avoir lu ce premier article, le prochain traitera de la gestion des routes et de la sécurité dans une application React.js. 😍

Sources :

Tags de
l'article

React.JS Front-end

Catégories de l'article

Javascript

Commentaires

Gourain

Il y a 1 semaine Répondre

Salut jean-smaug, merci pour tes remarques pertinentes ! C'est vrai que préfixer les alias par @ ou ~ serait mieux pour qu'il n'y ait pas de confusion dans les imports comme tu le dis. Pour ne pas s'embêter avec les imports à rallonge comme "components/MonCompo/MonCompo" je préfère, pour ma part, utiliser un plugin webpack comme "directory-named-webpack-plugin" ça éviter d'alourdir le projet de fichiers "index.js". Mais encore une fois les deux méthodes sont valables, c'est qu'une question de préférence. ;) Merci pour ton retour en tout cas ça fait plaisir ! :)

Il y a 1 semaine Répondre

Salut, J'ai qq remarques, qui j'espère te seront utiles. - Depuis CRA2 customize-cra vient en complément de react-app-rewired pour tout ce qui est config custo sans eject. (il existe des alternatives) - Pour les alias webpack j'aime assez l'approche de Vue qui est de les préfixer par un @ ou un ~. Ainsi on distingue les import venant des node_modules et notre code et par la même occasion on évite de possible conflits de nommages :) - Astuce : dans les compo tu peux créer un fichier index.js qui fait import MonCompo from "./MonCompo.js" export default MonCompo Et comme ça dans tes scènes, tu pourras te contenter d'importer "components/MonCompo" plutôt que "components/MonCompo/MonCompo". Après l'esprit de l'article étant "il n'y a pas une façon de faire" (c'est bien vrai). Voila de quoi ouvrir la discussion :) Bravo pour ton 1er article !

Gourain

Il y a 1 semaine Répondre

Bonjour ozee31, Merci pour ton commentaire je ne connaissais pas cette solution qui est effectivement plus simple. Par contre, je n'ai pas l'impression qu'on puisse ajouter plusieurs "alias" avec cette méthode contrairement avec webpack.

ozee31

Il y a 1 semaine Répondre

Merci pour l'article par contre sur la partie "Simplifer les imports : le problème des chemins relatifs" j'ai une solution beaucoup plus simple et élégante : Créer un fichier jsconfig.json avec comme code : { "compilerOptions": { "baseUrl": "src" }, "include": ["src"] } Et en plus vscode le comprend très bien

Articles liés