Table of Contents
Pourquoi une structure native de réaction modulaire est essentielle pour la scalabilité
Construire une application React Native qui exige gracieusement plus que d'écrire un code propre. À mesure que votre application se développe en fonctionnalités, en taille d'équipe et en base d'utilisateurs, l'arrangement initial du dossier peut devenir un goulot d'étranglement ou un catalyseur pour une vitesse de développement soutenue. Une architecture modulaire – où la base de code est divisée en modules indépendants et autonomes – aborde directement la complexité qui vient avec l'échelle. Sans structure délibérée, même un projet de taille moyenne peut succomber à des dépendances enchevêtrées, à une logique dupliquée et à des conflits douloureux de fusion.
- Développement indépendant[ – Les équipes peuvent travailler sur des modules séparés sans se mettre sur les orteils.
- Reutilisabilité à travers les écrans et les applications – Les composants, crochets et utilitaires partagés vivent dans des endroits dédiés.
- Essais isolés – Chaque module peut être testé isolément, réduisant le rayon de blason des régressions.
- L'adoption progressive de nouveaux modèles – Refactoriser ou migrer un module est beaucoup moins risqué que réécrire l'application entière.
- Modèles mentaux précis – Les nouveaux ingénieurs à bord plus rapidement lorsqu'ils peuvent raisonner sur les pièces apps sans lire la base de code complète.
Dans cet article, nous allons passer par une structure de projet testée en production, expliquer la responsabilité de chaque répertoire, et discuter des modèles qui maintiennent votre application React Native à jour car elle se développe au-delà de quelques écrans.
Principes fondamentaux d'une architecture autochtone à réaction modulaire
Avant de plonger dans la mise en page du dossier, il est utile d'établir quelques principes directeurs. Ces principes devraient informer chaque décision que vous prenez sur le lieu où placer un fichier et comment exposer sa fonctionnalité.
Séparation des préoccupations
Chaque module devrait avoir un seul travail bien défini. Par exemple, un ne devrait traiter que les appels API et la transformation des données pour les paramètres liés à l'utilisateur; il ne devrait jamais rendre l'interface utilisateur. De même, un composant ne devrait traiter que la présentation et la mise en page, et non pas récupérer les données du serveur.
Encapsulation
Les modules devraient exposer une surface publique minimale. Les fonctions internes d'aide, les sous-composants ou les modèles de gestion d'état qui ne sont pertinents qu'à l'intérieur d'un module devraient être gardées privées (p. ex. en les plaçant dans un sous-dossier ou en les nommant avec une convention de surbrillance).
Dépendances explicites
Plutôt que de compter sur des singletons globaux ou des importations implicites (comme -import depuis n'importe où), une structure modulaire encourage l'injection explicite de dépendances – soit par le biais de React Context, Redux Store, ou des paramètres de fonction simples.
Cohérence avec la Convention
Alors que chaque équipe a des préférences, une fois que vous choisissez une convention (nom de fichier, nicher un dossier, style d'exportation) vous devez l'appliquer de façon cohérente.
Structure recommandée du projet : Une plongée profonde
La structure suivante a été testée dans la production React Native apps allant d'une poignée d'écrans à trois chiffres modules de fonctionnalités. Il équilibre la simplicité avec la capacité d'échelle. We=ll suppose une base de code TypeScript—si vous utilisez un JavaScript simple, les mêmes principes s'appliquent.
my-react-native-app/
├── assets/
│ ├── fonts/
│ ├── images/
│ └── lottie/
├── src/
│ ├── components/ # Reusable UI primitives
│ ├── screens/ # Top-level route components
│ ├── navigation/ # Navigation configuration & linking
│ ├── services/ # API clients, data-fetching logic
│ ├── state/ # Global state (Redux, Zustand, etc.)
│ ├── hooks/ # Custom React hooks
│ ├── utils/ # Pure utility functions & constants
│ ├── types/ # TypeScript interfaces & enums
│ ├── config/ # Environment variables, feature flags
│ └── theme/ # Colors, typography, spacing tokens
├── tests/ # Integration & end-to-end tests
├── app.json
├── package.json
└── tsconfig.json
Let , examine chaque répertoire , le but et ce qui appartient à l'intérieur.
– Blocs réutilisables de construction d'interfaces utilisateur
Ce dossier contient des composants qui ne sont pas liés à un écran ou à une fonctionnalité spécifique. Exemples : , , , [, , , . Ils devraient être entièrement génériques : ils reçoivent des accessoires et rendent l'interface utilisateur sans connaître la logique d'entreprise de l'application. Si vous vous trouvez en train d'ajouter un accessoire comme à un générique , vous avez probablement besoin d'un composant plus spécifique.
Une erreur courante est de jeter toutes les pièces d'interface utilisateur possibles dans un dossier plat . À mesure que la bibliothèque grandit, envisager de regrouper les composants connexes en sous-dossiers :
- – Les widgets vraiment universels.
- – Champs d'entrée, cases à cocher, boutons radio.
- – Composants de visualisation des données.
Chaque composante devrait avoir son propre fichier de test (p. ex. ) et éventuellement une histoire de livre de contes pour les tests de régression visuelle.
– Composantes de la page de haut niveau
Les écrans sont les composants qui se plantent directement sur les itinéraires de votre pile de navigation. Chaque écran est composé d'un mélange de composants réutilisables et de composants spécifiques aux caractéristiques qui vivent à l'intérieur du dossier d'écran (ou d'un répertoire co-implanté). L'écran lui-même devrait être mince : il récupère les données, passe les accessoires et gère la mise en page de l'écran.
Convention de désignation : , , . Si vous avez de nombreux écrans, vous pouvez les regrouper par domaine de fonctionnalités :
- – LoginScreen, RegisterScreen, Mot de passe oubliéScreen
- – Écran principal, AnalytiqueÉcran, ReportsÉcran
– Routage & Liaison profonde
Vous pouvez configurer votre pile de navigation, onglet, tiroir et configurations de liaison. La séparation de la navigation des écrans et des composants vous permet de modifier l'ensemble du flux de navigation (par exemple, en échangeant un navigateur de pile avec un navigateur modal) sans toucher aucun code d'écran. Fichiers typiques:
- – Le navigateur de haut niveau qui décide de la pile à afficher (auth vs main).
- – La barre d'onglets inférieure.
- – Objet de configuration de lien profond pour la navigation de réaction.
- – Un renvoi au conteneur de navigation pour utilisation en dehors des composants (p. ex. en service).
Si votre application supporte les liens profonds depuis les notifications push ou les liens universels, ce dossier est la seule source de vérité pour la cartographie des itinéraires.
– API Appels & Logique d'affaires
Les services encapsulent toute communication avec les systèmes externes : API REST, GraphQL, localStorage, enregistrement de notification push, etc. Un service est généralement une classe ou un ensemble de fonctions qui prennent des paramètres et des promesses de retour. Par exemple :
- – connexion, déconnectation, jeton rafraîchissant.
- – fetchProfile, updateProfile, uploadAvatar.
- – trackEvent, identifieUser.
Les services ne devraient pas importer de React ou de code UI. Ils peuvent cependant utiliser des fonctions d'aide de et des types de . Cela les rend testables avec des tests d'unité pure et facile à simuler dans les tests d'intégration.
Pour la récupération de données, de nombreuses équipes préfèrent désormais utiliser React Query ou SWR, qui gèrent la mise en cache et la refechage de fond. Dans ces cas, vous pouvez placer les crochets de requête à l'intérieur mais les appels API sous-jacents vivent toujours dans .
– Gestion mondiale de l'État
Ce répertoire contient la solution de votre état global choisi : Redux store, Redux Toolkit slices, Zustand stores ou Recoil atomes. Gardez chaque fournisseur de tranche ou de contexte de magasin dans son propre fichier, nommé par domaine. Exemple pour Redux Toolkit :
- – configureStore, réducteur de racine.
- – intergiciel personnalisé (par exemple, logage, analyse).
Si vous utilisez React Context, placez vos fournisseurs et les crochets de contexte ici. Garder l'état global isolé empêche le mélange accidentel de la logique d'interface utilisateur avec la logique d'état.
– Crochets personnalisés
Encapsuler la logique réutilisable et l'état dans les crochets personnalisés.
- (piste de savoir si l'application est en avant-plan/arrière-plan)
- (wraps auth état et services téléphoniques)
Les crochets spécifiques à un seul écran devraient vivre co-situés avec cet écran, et non dans le dossier global .
– Constantes de services publics et d'ampli purs
Ce dossier contient des fonctions ou des constantes qui sont pures, apatrides et ne dépendent pas de Réaction ou de tout état d'application. Par exemple:
- (URL de base API, valeurs de timeout, touches de drapeau)
Gardez ces petits et objectifs. Évitez les fichiers -Kitchen évier , qui contiennent des utilitaires non liés. Si vous trouvez plus d'une poignée d'aide, les casser en fichiers séparés.
– Définitions de type de script
Centralisez vos interfaces TypeScript, vos alias et vos enums ici. Exemples communs:
- – listes de paramètres pour chaque navigateur.
- – User, UserProfile, UserSettings types.
- – enveloppe de réponse générique API, types de pagination.
- – types spécifiques à la marque.
L'utilisation d'une seule source de vérité pour les types empêche les incohérences et facilite la refactoration lorsque le schéma du moteur change.
– Drapeaux de l'environnement &
Les applications autochtones réagissent souvent à des configurations différentes par environnement (développement, mise en scène, production). Gardez cette logique ici, souvent en utilisant ou des variables d'environnement.
- – une carte des drapeaux booléens pour activer/désactiver les caractéristiques de développement.
– Jetons de conception & Theming
Un fichier thématique exporte des constantes pour les couleurs, la typographie, l'espacement, les ombres et les points d'arrêt. De nombreuses équipes utilisent une bibliothèque comme ou qui consomment ces jetons. Pour l'accessibilité, fournir un fichier thématique en mode clair et sombre. Exemple :
- – objet par défaut du thème.
– Ressources statiques
Conservez tous les fichiers statiques importés sous condition ou au moment de la construction. Cela inclut les polices, images, animations Lottie, fichiers JSON, et similaires. La structure par type de ressource aide votre bundler (Metro) à les résoudre correctement.
– Essais d'intégration & E2E
Alors que les tests unitaires devraient vivre à côté du code qu'ils testent (p. ex. ), les fichiers de test d'intégration et de bout en bout appartiennent ici. Utilisez Detox ou Appium pour E2E et créez des profils de test pour différents parcours utilisateurs.
Mise en œuvre de la structure dans la pratique
Maintenant que vous comprenez la théorie, voici une approche pratique étape par étape pour mettre en place cette structure dans un projet nouveau ou existant React Native.
Étape 1: Initialiser l'arbre du dossier
Créez la structure du répertoire en utilisant votre terminal ou IDE. Pour un nouveau projet, utilisez d'abord, puis supprimez par défaut et recréez comme point d'entrée qui importe . Cela maintient le minimum de racine.
Étape 2: Mettre en place la navigation tôt
Installez [et créez un [dans .Définissez vos itinéraires d'écran initiaux.
Étape 3: Créer le thème et les constantes
Avant d'écrire des composants, établissez vos jetons de conception dans et constantes dans . Cela garantit que chaque développeur utilise des valeurs cohérentes dès le premier jour.
Étape 4: Construire un composant réutilisable
Choisissez un composant simple comme et placez-le dans . Écrivez son fichier de test. Exportez-le et utilisez-le dans un écran de placeholder. Cela valide que votre pipeline de construction fonctionne avec la structure du dossier.
Étape 5: Créer un calque de service
Si votre application communique avec une API, créez un dans qui configure ou avec URL de base et intercepteurs. Ajoutez ensuite un service spécifique au domaine (par exemple, .
Étape 6 : Ajouter la gestion de l'État
Décidez d'un outil d'état (Redux Toolkit, Zustand, etc.) et mettez-le en . Connectez-le à l'application dans .
Étape 7 : Refacteur Code existant peu à peu
Si vous migrez un projet existant, déplacez les fichiers d'un répertoire à la fois, en commençant par les parties les plus stables (thème, constantes, services). Utilisez des outils comme et gardez vos tests en vert. Il vaut mieux passer une semaine à refactoriser que de vivre avec une base de code enchevêtrée pendant des mois.
Considérations avancées pour les applications à grande échelle
À mesure que votre équipe et votre base de code grandissent au-delà de 20 à 30 développeurs, la structure de base basée sur les couches peut avoir besoin d'une augmentation.
Modules basés sur les caractéristiques (dossiers de caractéristiques)
Au lieu de se séparer par un rôle technique (composante, service, écran), vous regroupez chaque fichier lié à un domaine d'affaires en un seul dossier de haut niveau. Exemple :
src/
features/
auth/
components/
screens/
services/
state/
hooks/
types/
profile/
components/
screens/
services/
state/
hooks/
types/
shared/
components/
utils/
hooks/
Cette approche permet de garder chaque fonction entièrement encapsulée et plus facile à raisonner. Elle fonctionne mieux lorsque les caractéristiques sont vraiment indépendantes et peuvent être développées par des équipes distinctes. L'inconvénient est qu'elle peut conduire à une certaine duplication de composants génériques si ce n'est discipliné sur le déplacement de pièces partagées à .
Monorepos avec bibliothèques partagées
Si vous maintenez plusieurs applications React Native (visant le client, admin, marque blanche), considérez un monorepo géré avec Nx ou Turborepo. Placez les composants, crochets et utilitaires de React Native partagés dans une bibliothèque que les deux applications consomment. Cela exploite la structure modulaire à travers les applications et fait valoir une seule source de vérité pour votre système de conception. La structure principale de l'application décrite ci-dessus s'applique toujours, mais le dossier peut simplement réexporter de la bibliothèque partagée.
Chargement par lassature du code &
React Native ne supporte pas les importations dynamiques hors-de-la-boîte, mais des bibliothèques comme et le support Hermes peuvent aider. Structurez vos écrans de sorte que chaque écran soit un module séparé chargé paresseux. Cela réduit la taille du paquet initial et améliore le temps de démarrage pour les grandes applications.
Pratiques exemplaires pour la conservation à long terme
Même la meilleure structure de dossier échouera sans habitudes disciplinées. Intégrez ces pratiques dans votre workflow quotidien.
- Enforcer avec le lintage – Utiliser des règles comme pour empêcher les importations accidentelles de modules croisés, p.ex. un service ne devrait jamais importer un composant.
- – Chaque dossier de module devrait comporter un sous-dossier ou un fichier .test co-implanté. Tester les services en isolation, tester les crochets avec et tester les écrans avec un magasin de simulation.
- Soyez explicites – Évitez de compter sur des fournisseurs globaux implicites. Si un écran a besoin de l'état auth, passez-le via des accessoires ou dans un contexte clairement documenté. Cela facilite la refacturation plus tard.
- Utilisez le mode TypeScript strict – Définir dans tsconfig. Cela capture les problèmes de sécurité nuls et encourage le typage approprié des limites des modules.
- Revoir la santé de la structure trimestrielle – Lorsque des fonctionnalités sont ajoutées, vous pouvez remarquer que les dossiers sont trop grands.
- Documentez vos conventions – Créez un qui explique la structure du dossier, les conventions de noms et les règles d'importation.
Pour plus de détails, la documentation React Native architecture fournit des conseils sur le filetage, le pont et les TurboModules – bien que non directement sur la structure du projet, comprendre la plate-forme sous-jacente aide à prendre des décisions plus intelligentes en matière de modularité.
Conclusion
Une structure modulaire de projet React Native n'est pas une puce argentée, elle nécessite des efforts délibérés pour concevoir et entretenir. Mais le gain est immense : plus rapide à bord, plus sûr à refactoriser, moins de conflits de fusion, et la capacité d'écheller votre application sans la réécrire de zéro. Commencez par la mise en page basée sur la couche décrite ci-dessus, imposez la séparation des préoccupations avec le doublage et les tests, et évoluer vers des modèles basés sur des fonctionnalités ou monorepo selon vos besoins.