Débuter avec Redux Apprendre par l'exemple

Redux vous aide à gérer l'état en le configurant au niveau mondial. Dans le didacticiel précédent, nous avons examiné de près l'architecture Redux et les composants intégrés de Redux tels que les actions, les créateurs d'action, le magasin et les réducteurs.. 

Dans ce deuxième article de la série, nous allons renforcer notre compréhension de Redux et nous appuyer sur ce que nous savons déjà. Nous allons commencer par créer une application Redux réaliste - une liste de contacts - plus complexe qu'un compteur de base. Cela vous aidera à renforcer votre compréhension du concept de magasin unique et de réducteurs multiples que j'ai présenté dans le didacticiel précédent. Ensuite, nous parlerons de la liaison de votre état Redux avec une application React et des meilleures pratiques à prendre en compte lors de la création d'un projet à partir de zéro.. 

Cependant, si vous n'avez pas lu le premier post, vous pouvez quand même suivre si vous connaissez les bases de Redux. Le code du tutoriel est disponible dans le référentiel et vous pouvez l'utiliser comme point de départ.. 

Créer une liste de contacts en utilisant Redux

Nous allons créer une liste de contacts de base avec les fonctionnalités suivantes:

  • afficher tous les contacts
  • rechercher des contacts
  • récupérer tous les contacts du serveur
  • ajouter un nouveau contact
  • pousser les nouvelles données de contact sur le serveur

Voici à quoi ressemblera notre application:

Produit final - Liste de contacts Voir


Produit final - Ajouter une vue de contact

Couvrir tout d'un trait est difficile. Donc, dans cet article, nous allons nous concentrer uniquement sur la partie Redux consistant à ajouter un nouveau contact et à afficher le contact nouvellement ajouté. Du point de vue de Redux, nous allons initialiser l'état, créer le magasin, ajouter des réducteurs et des actions, etc.. 

Dans le prochain didacticiel, nous allons apprendre à connecter React et Redux et à distribuer des actions Redux à partir d’un serveur React. Dans la dernière partie, nous allons nous concentrer sur les appels API à l’aide de Redux. Cela inclut la récupération des contacts du serveur et la demande du serveur lors de l'ajout de nouveaux contacts. En dehors de cela, nous allons également créer une fonctionnalité de barre de recherche qui vous permet de rechercher tous les contacts existants.. 

Créer une esquisse de l'arbre d'état

Vous pouvez télécharger l'application de démonstration react-redux à partir de mon référentiel GitHub. Cloner le repo et utiliser le v1 branche comme point de départ. le v1 branch est très similaire au modèle create-react-app. La seule différence est que j'ai ajouté quelques répertoires vides pour organiser Redux. Voici la structure du répertoire.

. ├── package.json ├── public ├── README.md actions src ├── App.js composants conteneurs index.js ├── réducteurs magasin └── fil.lock 

Alternativement, vous pouvez créer un nouveau projet à partir de zéro. Quoi qu'il en soit, vous devez avoir installé une base chauffante de base react et redux avant de pouvoir commencer.. 

C'est une bonne idée d'avoir d'abord un aperçu de l'arbre d'état. À mon avis, cela vous fera gagner beaucoup de temps à long terme. Voici un aperçu de l'arborescence d'état possible. 

const initialState = contacts: contactList: [], newContact: name: ", nom de famille:", email: ", adresse:", téléphone: ", ui: // Tous les états liés à l'interface utilisateur ici. Par exemple: masquer / afficher les modaux, // cochez la case etc.  

Notre magasin doit avoir deux propriétés-Contacts et ui. La propriété contacts prend en charge tous les états liés aux contacts, alors que la ui gère l'état spécifique à l'interface utilisateur. Il n’existe aucune règle stricte dans Redux qui vous empêche de placer le ui objet en tant que sous-état de Contacts. N'hésitez pas à organiser votre état de manière à ce qu'il ait un sens pour votre application.. 

La propriété de contacts a deux propriétés imbriquées à l'intérieur-liste de contacts et nouveau contact. le liste de contacts est un tableau de contacts, alors que nouveau contact stocke temporairement les coordonnées pendant le remplissage du formulaire de contact. Je vais l'utiliser comme point de départ pour la création de notre superbe application de liste de contacts. 

Comment organiser Redux

Redux n'a pas d'opinion sur la structure de votre application. Il existe quelques modèles populaires, et dans ce tutoriel, je parlerai brièvement de certains d'entre eux. Mais vous devez choisir un motif et vous y tenir jusqu'à ce que vous compreniez parfaitement comment toutes les pièces sont liées les unes aux autres..

Le modèle le plus courant que vous trouverez est la structure de fichiers et de dossiers de style Rails. Vous aurez plusieurs répertoires de niveau supérieur comme ceux ci-dessous:

  • Composants: Un endroit pour stocker les composants stupides de React. Peu importe que vous utilisiez Redux ou non.
  • conteneurs: Répertoire des composants intelligents React qui distribue des actions au magasin Redux. La liaison entre redux et react aura lieu ici. 
  • actes: Les créateurs d'action iront dans ce répertoire. 
  • réducteurs: Chaque réducteur obtient un fichier individuel et vous placerez toute la logique du réducteur dans ce répertoire..
  • le magasin: La logique d'initialisation de l'état et de configuration du magasin ira ici. 

L'image ci-dessous montre à quoi notre application pourrait ressembler si nous suivions ce modèle:

Le style Rails devrait fonctionner pour les applications de petite et moyenne taille. Toutefois, lorsque votre application grandit, vous pouvez envisager d'adopter l'approche de style de domaine ou d'autres alternatives populaires étroitement liées au style de domaine. Ici, chaque entité aura son propre répertoire et tout ce qui se rapporte à cette entité (domaine) sera à l'intérieur. L'image ci-dessous compare les deux approches, style Rails à gauche et domaine à droite. 

Pour l'instant, allez-y et créez des répertoires pour Composants, des conteneurs, le magasin, réducteurs, et action. Commençons par le magasin. 

Magasin unique, réducteurs multiples

Créons un prototype pourla le magasin et le réducteur premier. De notre exemple précédent, voici à quoi ressemblerait notre magasin: 

const store = createStore (réducteur, contacts: contactlist: [], newContact: , ui: isContactFormHidden: true) const réducteur = (état, action) => switch (action.type) case "HANDLE_INPUT_CHANGE": pause; case "ADD_NEW_CONTACT": pause; case "TOGGLE_CONTACT_FORM": break;  état de retour; 

L'instruction switch a trois cas qui correspondent aux trois actions que nous allons créer. Voici une brève explication de ce que les actions sont destinées à. 

  • HANDLE_INPUT_CHANGE: Cette action est déclenchée lorsque l'utilisateur entre de nouvelles valeurs dans le formulaire de contact.
  • AJOUTER UN NOUVEAU CONTACT: Cette action est envoyée lorsque l'utilisateur soumet le formulaire.
  • TOGGLE_CONTACT_FORM: Il s'agit d'une action de l'interface utilisateur qui prend en charge l'affichage / le masquage du formulaire de contact.. 

Bien que cette approche naïve fonctionne, à mesure que l’application se développe, l’utilisation de cette technique présentera quelques inconvénients..

  1. Nous utilisons un seul réducteur. Même si un seul réducteur semble correct pour le moment, imaginez avoir toute votre logique métier sous un très grand réducteur..  
  2. Le code ci-dessus ne suit pas la structure Redux que nous avons discuté dans la section précédente.

Pour résoudre le problème de réducteur unique, Redux utilise une méthode appelée combineRéducteurs qui vous permet de créer plusieurs réducteurs, puis de les combiner en une seule fonction de réduction. La fonction combineRéducteurs améliore la lisibilité. Donc, je vais diviser le réducteur en deux contactsRéducteur et un uiReducer

Dans l'exemple ci-dessus, createStore accepte un optionnel deuxième argument qui est l'état initial. Cependant, si nous allons diviser les réducteurs, nous pouvons déplacer l'ensemble Etat initial vers un nouvel emplacement de fichier, par exemple réducteurs / initialState.js. Nous allons ensuite importer un sous-ensemble de Etat initial dans chaque fichier réducteur. 

Fractionnement du réducteur 

Restructurons notre code pour résoudre les deux problèmes. Tout d’abord, créez un nouveau fichier appelé store / createStore.js et ajoutez le code suivant:

importer createStore à partir de 'redux'; importer rootReducer de '… / réducteurs /'; / * Crée une fonction appelée configureStore * / export, fonction par défaut configureStore () return createStore (rootReducer);  

Ensuite, créez un réducteur de racine dans réducteurs / index.js comme suit:

importer combineReducers de 'redux' importer contactsReducer de './contactsReducer'; importer uiReducer de './uiReducer'; const rootReducer = combineReducers (contacts: contactsReducer, ui: uiReducer,) export default rootReducer;

Enfin, nous devons créer le code pour le contactsRéducteur et uiReducer.

réducteurs / contactsRéducteur.js

importer initialState de './initialState'; exporter la fonction par défaut contactReducer (state = initialState.contacts, action) switch (action.type) / * Ajouter des contacts au tableau d'état * / case "ADD_CONTACT": return … state, contactList: [… state.contactList, state.newContact] / * Traitement de l'entrée pour le formulaire de contact. La charge utile (modifications d'entrée) est fusionnée avec l'objet newContact * / case "HANDLE_INPUT_CHANGE": return … state, newContact: … state.newContact,… action.payload default: return state; 

réducteurs / uiReducer.js

importer initialState de './initialState'; fonction par défaut d'exportation uiReducer (state = initialState.ui, action) switch (action.type) / * Afficher / masquer le formulaire * / case "TOGGLE_CONTACT_FORM": return … state, isContactFormHidden:! state.isContactFormHidden default : état de retour;  

Lorsque vous créez des réducteurs, gardez toujours à l'esprit les points suivants: un réducteur doit avoir une valeur par défaut pour son état et doit toujours renvoyer quelque chose. Si le réducteur ne respecte pas cette spécification, vous obtiendrez des erreurs..

Puisque nous avons couvert beaucoup de code, examinons les changements que nous avons apportés à notre approche:

  1. le combiner des réducteurs un appel a été introduit pour relier les réducteurs de split.
  2. L'état de la ui objet sera manipulé par uiReducer et l'état des contacts par le contactsRéducteur
  3. Pour garder les réducteurs purs, des opérateurs d'écartement ont été utilisés. La syntaxe à trois points fait partie de l'opérateur d'étalement. Si vous n'êtes pas à l'aise avec la syntaxe de propagation, vous devriez envisager d'utiliser une bibliothèque comme Immutability.js..
  4. La valeur initiale n'est plus spécifiée comme argument optionnel pour createStore. Au lieu de cela, nous avons créé un fichier séparé appelé initialState.js. Nous importons Etat initial puis en définissant l'état par défaut en faisant state = initialState.ui

Initialisation d'état

Voici le code pour le réducteurs / initialState.js fichier.

const initialState = contacts: contactList: [], newContact: name: ", nom de famille:", email: ", adresse:", téléphone: ",, ui: isContactFormHidden: true export default initialState;

Actions et créateurs d'actions

Ajoutons quelques actions et créateurs d’actions pour ajouter des modifications au formulaire de traitement, ajouter un nouveau contact et basculer l’état de l’interface utilisateur. Si vous vous souvenez, les créateurs d'action ne sont que des fonctions qui renvoient une action. Ajoutez le code suivant dans actions / index.js.

export const addContact = () => return type: "ADD_CONTACT", export const handleInputChange = (nom, valeur) => return type: "HANDLE_INPUT_CHANGE", charge utile: [nom]: valeur export const toggleContactForm = () => return type: "TOGGLE_CONTACT_FORM",

Chaque action doit renvoyer une propriété de type. Le type est comme une clé qui détermine quel réducteur est appelé et comment l'état est mis à jour en réponse à cette action. La charge est optionnelle et vous pouvez l'appeler comme bon vous semble. 

Dans notre cas, nous avons créé trois actions.

le TOGGLE_CONTACT_FORM n'a pas besoin d'une charge utile, car chaque fois que l'action est déclenchée, la valeur de ui.isContactFormHidden se change. Les actions à valeur booléenne ne nécessitent pas de charge utile. 

le HANDLE_INPUT_CHANGE l'action est déclenchée lorsque la valeur du formulaire change. Ainsi, imaginons par exemple que l'utilisateur remplisse le champ email. L'action reçoit alors "email" et "[email protected]" en tant qu'entrées, et la charge utile remise au réducteur est un objet qui ressemble à ceci:

email: "[email protected]"

Le réducteur utilise ces informations pour mettre à jour les propriétés pertinentes du nouveau contact Etat. 

Envoi d'actions et inscription au magasin

La prochaine étape logique consiste à répartir les actions. Une fois les actions envoyées, l’état change en conséquence. Pour répartir les actions et obtenir l'arborescence d'état mise à jour, Redux propose certaines actions de magasin. Elles sont:

  • dispatch (action): Distribue une action pouvant potentiellement déclencher un changement d'état. 
  • getState (): Retourne l'arborescence d'état actuelle de votre application.
  • abonné (auditeur): Un écouteur de modification appelé chaque fois qu'une action est envoyée et qu'une partie de l'arborescence d'état est modifiée. 

Dirigez-vous vers le index.js déposer et importer le configureStore fonction et les trois actions que nous avons créées précédemment:

importer Réagir de 'réagir'; importer render de 'react-dom'; importer l'application depuis './App'; / * Importer le magasin Redux et les actions * / import configureStore depuis './store/configureStore'; import toggleContactForm, handleInputChange depuis './actions';

Ensuite, créez un le magasin object et ajoutez un écouteur qui enregistre l'arbre d'état chaque fois qu'une action est envoyée:

const store = configureStore (); // Notez que subscribe () renvoie une fonction permettant de désenregistrer le écouteur. Const unsubscribe = store.subscribe (() => console.log (store.getState ()))

Enfin, envoyez quelques actions:

/ * renvoie isContactFormHidden renvoie false * / store.dispatch (toggleContactForm ()); / * renvoie isContactFormHidden renvoie false * / store.dispatch (toggleContactForm ()); / * met à jour l'état de l'objet contacts.newContact * / store.dispatch (handleInputChange ('email', '[email protected]')) unsubscribe;

Si tout fonctionne correctement, vous devriez le voir dans la console du développeur..

C'est tout! Dans la console du développeur, vous pouvez voir le magasin Redux en cours de journalisation, ainsi vous pouvez voir comment il change après chaque action..

Résumé

Nous avons créé une application Redux simple pour notre application géniale de liste de contacts. Nous avons appris comment utiliser les réducteurs, diviser les réducteurs pour rendre la structure de notre application plus propre et rédiger des actions visant à transformer le magasin en mutation.. 

Vers la fin du message, nous avons souscrit à la boutique en utilisant le store.subscribe () méthode. Techniquement, ce n'est pas la meilleure façon de faire avancer les choses si vous allez utiliser React avec Redux. Il existe d'autres moyens optimisés de connecter le front-end de réaction à Redux. Nous allons couvrir ceux dans le prochain tutoriel.