Fermer

janvier 24, 2019

Comment utiliser les crochets de réaction basiques pour réducteurs


Dans le prochain article de notre série sur les crochets React, nous allons apprendre à utiliser les réducteurs en parcourant un exemple d'application todo. Soyez "accro" en jetant un coup d'œil à useReducer React Hook!

Dans les derniers articles, nous nous sommes familiarisés avec React Hooks. Dans notre premier article, nous avons découvert State & Effect qui nous permet d'accéder à l'état local et aux effets secondaires dans un composant fonctionnel.

Dans notre deuxième article, nous avons étudié l'API contextuelle de React . ] et a montré comment l'utiliser avec des classes régulières dans un composant de profil. L'API de contexte nous permet de partager des données avec des composants enfants à l'aide d'un fournisseur et d'un consommateur. Mais avant que Hooks n'arrive, cela n'était pas réalisable avec des composants fonctionnels. Nous avons donc refactored avec le hook useContext . Cela impliquait de changer les classes de notre projet en composants fonctionnels, puis de supprimer toutes les classes de notre démo, rendant la lecture plus facile avec moins de syntaxe. Dans les deux cas, nous avons appris que les crochets nous permettaient de migrer notre code de classes à des composants fonctionnels, car les crochets ne pouvaient être utilisés que dans des composants fonctionnels ou d'autres crochets.

Dans cet article, nous utilisons ce que nous avons appris et appliquons ces connaissances à une démonstration plus avancée utilisant le crochet useReducer . Comprendre le crochet de base useState peut nous préparer à en apprendre davantage sur useReducer . Par conséquent, si vous n'avez pas lu le premier article de cette série, il est vivement encouragé avant de continuer.

Si vous avez programmé React sous un rocher ou que vous venez de prendre React, Redux est le moyen le plus répandu de travailler avec des données unidirectionnelles dans React et est également encouragé par l'équipe de React. C'est devenu un standard dans la communauté et l'écosystème de React, et c'est pourquoi React a construit useReducer dans React, ce qui vous permet d'utiliser le modèle Redux sans dépendre de la bibliothèque Redux pour créer un simple état d'interface utilisateur.

Dans React, votre seul travail est de gérer l’État

Eh bien, pas tout à fait, et je conviens que cette rubrique est un peu banale. Toutefois, selon de nombreux développeurs, lors de la création d’applications React, vous devez avant tout prendre en compte l’état actuel de votre état et votre interface utilisateur. Ceci déterminera la façon dont vous gérerez son état et construirez votre application autour d'elle.

Ne vous inquiétez pas, nous utilisons la séparation des problèmes dans une application React appropriée, ainsi l'état et la l'interface utilisateur sont compartimentés. Nous pouvons généralement simplement construire une interface utilisateur, puis nous concentrer sur la gestion de son état. Ceci détermine ce que nous allons faire ensuite et nous devons décider exactement de ce qui se passe chaque fois que l'interface utilisateur est en interaction et comment cela pourrait transformer l'état que nous gérons.

A Primer on Reducers

Parlons de la différence entre un Redux. state réducteur et la méthode JavaScript Array.prototype.reducer .

L'exemple de prototype de matrice canonique est une fonction de somme. Lorsque nous appelons le réducteur sur un tableau contenant uniquement des nombres, nous pouvons renvoyer une valeur unique résumant toutes les valeurs du tableau et renvoyant le total sous la forme d'une valeur unique. Le réducteur peut également être alimenté par une valeur initiale. Examinons brièvement un code qui illustre la méthode de réduction à partir de la méthode JavaScript Array.prototype appelée reduction () .

 const votesByDistrict = [250, 515, 333, 410];
const réducteur = (accumulateur, currentValue) => {
  return accumulator + currentValue;
}

console.log (votesByDistrict.reduce (réducteur));
// résultat attendu: 1508

// et ci-dessous, nous ajoutons simplement une valeur pour commencer:

console.log (votesByDistrict.reduce (réducteur, 777));
// résultat attendu: 2285 

Si vous avez travaillé avec des réducteurs Redux, vous voyez probablement une similitude dans l'exemple que je viens d'expliquer et comment fonctionne les réducteurs Redux. Vous pouvez facilement voir pourquoi le réducteur Redux partage le même nom.

La fonction sum est l'exemple le plus simple, mais à l'intérieur de ce réducteur, vous pouvez effectuer le travail que vous souhaitez de manière itérative entre ces accolades. Considérez-le comme une recette selon laquelle, quel que soit le contenu du tableau, cette fonction produit toujours le même résultat en considérant la même entrée. Une fonction pure. C'est un concept puissant, en particulier lorsqu'il est utilisé pour gérer l'état d'une application.

Le réducteur Redux, similaire au réducteur Arrays, renvoie l'accumulation de quelque chose – dans notre cas, un "état" basé sur toutes les actions précédentes et actuelles et les modifications d'état.

Les réducteurs de style Redux agissent donc comme des réducteurs d’État. Il reçoit les actions des états et . L'état est réduit (accumulé) en fonction du type d'action, puis renvoyé en tant que nouvel état. Chaque fois que nous réduisons, nous pouvons appeler cette opération un cycle unique.

Comme dans la cuisson d’une sauce bordelaise à la bordelaise, nous partons de nombreux ingrédients. Beurre, échalotes, veau, poivre et bien sûr vin. Tous ces ingrédients sont combinés ensemble dans une casserole et mijoté ou réduit (réduit). À plusieurs reprises, en suivant les mêmes étapes, en utilisant les mêmes ingrédients, les mêmes quantités, le même réchaud et les mêmes températures, nous devrions obtenir le même résultat à chaque fois. Une sauce merveilleusement géniale. Toujours en train de se demander d'où vient le nom réducteur?

Construisons un exemple de travail

Nous allons créer une application Todo. Pour commencer, nous voulons que notre liste Todo contienne un élément Todo initial qui dit simplement: "Commencez."

Lorsque nous ajoutons un nouvel élément Todo, le processus consiste à envoyer une action en premier.

Cette action est gérée par une fonction Reducer. Notre type d'action est ADD_TODO et notre fonction de réduction active ces types. Lorsque la fonction de réduction remarque le type ADD_TODO elle agit en supprimant l'ancien état et en ajoutant notre nouvel élément Todo à la fin, puis en renvoyant le nouvel état.

Une autre action que nous pourrions créer pourrait être COMPLETE_TODO ou, mieux encore, TOGGLE_COMPLETE . J'aime ce dernier car il nous donne la possibilité d'être incomplet, pour ainsi dire, si l'utilisateur clique sur le mauvais Todo.

Dans ce cas, notre réducteur n'ajoute aucun nouvel élément à la liste, il modifie une propriété d'un élément existant de Todo. Alors reprenons là où nous nous sommes arrêtés ci-dessus. Si nous avons commencé avec un Todo qui dit "Commencez" puis nous ajoutons un nouveau Todo: "Faites une pause", notre nouvel état devrait ressembler à ceci:

 {
  id: 1,
  nom: 'Commencez',
  complet: faux
},
{
  id: 2,
  nom: 'Faites une pause',
  complet: faux
} 

Notez que chaque Todo a plusieurs propriétés, l'une d'entre elles, un id . Ceci est une clé unique et nous l'utilisons spécifiquement pour savoir quel Todo nous voulons mettre à jour.

Le travail de réducteur pour TOGGLE_COMPLETE consiste à mettre à jour la propriété complète à partir de sa valeur actuelle de false à la valeur opposée de true . Une fois que cela est fait, toutes les modifications seront propagées à tous les composants qui utilisent cet élément d’état, ce qui les mettra à jour. Notre liste peut être considérée comme l'auditeur et une fois que nous avons ajouté, complété ou supprimé un Todo, la liste devrait refléter immédiatement ces nouveaux changements.

Ainsi, chaque propriété terminée de notre Todo commençant comme false si nous appelons TOGGLE_COMPLETED sur le Todo avec l'id de 1 notre état devrait être mis à jour pour ressembler à ce qui suit:

 {
  id: 1,
  nom: 'Commencez',
  complete: true // Nous l'avons changé!
},
{
  id: 2,
  nom: 'Faites une pause',
  complet: faux
} 

Nous venons de décrire (bien que dans un exemple simple) l'ensemble du cycle Redux, également appelé flux de données unidirectionnel.

Cela n'était pas facilement réalisable dans React sans une bibliothèque comme Redux. Mais maintenant, grâce à l’arrivée de Hooks, nous pouvons facilement implémenter le schéma réducteur Redux dans n’importe quelle application React sans utiliser une bibliothèque comme Redux.

J'admets que l'utilisation de state de cette manière devrait probablement être réservée au travail avec l'état interne, ce qui ne signifie pas que les grandes applications où l'état est géré par Redux sont maintenant obsolètes. Ce n'est pas le cas ici. Mais cela donne aux développeurs de React un moyen clair et concis de style Redux pour gérer immédiatement l’état interne sans installer de dépendances.

L’État que nous allons gérer

Traditionnellement, dans Redux, une décision sur la manière de classer l’état et de le classer le stocker était l'une des plus grandes questions du point de vue d'un débutant. Il s'agit en fait de la première question de leur FAQ Redux et voici ce qu'ils déclarent:

Il n'y a pas de «bonne» réponse à cela. Certains utilisateurs préfèrent conserver chaque donnée dans Redux afin de conserver à tout moment une version entièrement sérialisable et contrôlée de leur application. D'autres préfèrent conserver un état non critique ou un état d'interface utilisateur, tel que "est-ce que cette liste déroulante est actuellement ouverte", dans l'état interne d'un composant.

Les crochets sont puissants dans la couche de l'application où nous gardons une trace du type "est menu déroulant ouvert "et" est le menu fermé. " Nous pouvons prendre en charge la bonne gestion des données de l'interface utilisateur de la même manière que Redux sans laisser le cœur de React.

Dans une machine dont la seule responsabilité est de changer et d'ajuster en permanence l'état, le réducteur est la partie différente à chaque opération. . C'est la logique qui incrémente un compteur ou gère un objet complexe qui change pour avoir des ramifications sur l'état actuel. Donner accès à cela ainsi qu'à setState à partir de composants fonctionnels est la dernière pièce du puzzle – et en même temps, la première pièce d'un nouveau puzzle.

Voyons comment nous gérons le très simple Todo. application type. C'est un exemple parfait à des fins de démonstration. Voici les règles de notre application Todo:

Nous allons avoir besoin de quelques pièces en mouvement pour créer un cas simple d'utilisation dans le monde réel, en utilisant useReducer. Nous devrons suivre la façon dont notre état est modifié et mis à jour à l'aide d'actions telles que "ajouter", "compléter" et "effacer". En utilisant un modèle familier de Redux, nous associons généralement chacun de ces processus à un type d'action particulier géré par un répartiteur:

  • Un champ de formulaire permettant de saisir une tâche
  • Un répartiteur gérant notre formulaire lorsqu'il le soumet.
  • Un objet réel qui contient nos tâches
  • Un composant de tâche réel qui englobe tout
  • Un réducteur réel qui gère la modification de notre état

Commençons par additionner et composer toutes ces pièces. En général, je ne décris pas comment configurer un projet React, car cela peut se faire de différentes manières. J'aime plutôt donner à mes utilisateurs une démonstration StackBlitz qu'ils peuvent créer et travailler parallèlement au didacticiel. Une fois créé, ce projet vous appartient comme vous le souhaitez. Vous pouvez utiliser ces informations et ce code comme bon vous semble.

Nous commençons par un tout nouveau projet et pour ce tutoriel, nous allons tout faire dans le fichier index.js . Une fois terminé, vous souhaiterez peut-être extraire chaque élément de logique et tous les composants dans leurs propres fichiers. C'est un excellent exercice, surtout pour les débutants.

Dans notre exemple simple, nous aurons le composant Todo que nous créons comme composant de niveau racine de notre application, qui ressemblerait à ce qui suit:

 import React de 'réagir';
importer {render} de 'react-dom';
import './style.css';

const Todo = () => {
  revenir (
    <>
      Todo va ici
    >
  )
}

render (document.getElementById ('racine'));

Mais le code avec lequel je veux que vous commenciez est un peu plus avancé. J'ai ajouté suffisamment pour que nous puissions commencer, y compris un formulaire et un champ de saisie qui ne sont pas encore soumis. J'ai également ajouté une liste de styles et une partie de Json, que nous pouvons utiliser comme germes pour générer des éléments Todo afin de vérifier que notre liste s'affiche et que la forme de nos données est conforme à notre code HTML.

fourchette cette StackBlitz Demo à suivre avec le tutoriel. La démo de StackBlitz contient un bouton fork (fourchette) – une fois que vous avez cliqué dessus, vous pouvez lui attribuer un nouveau nom, ce qui créera un clone de mon point de départ sur lequel vous pourrez travailler.

Maintenant que nous Pour que le projet soit lancé, nous effectuerons notre premier changement en important le crochet useReducer de React. Mettez à jour la première ligne du fichier comme suit:

 import React, {useReducer} from 'react';

Nous devons ajouter notre appel au réducteur useReducer maintenant. Il prend les actions et comme arguments. Nous assignons cela à un objet de tableau qui est un tuple (deux valeurs) – il s'agit de destructuring car useReducer () lui correspond comme valeur de retour:

Ajoutez la ligne suivante juste au-dessus de l'instruction de retour dans le composant Todo:

const [todos, dispatch] = useReducer (todoReducer, initialState);

items est l'élément d'état qui est le suivant. liste actuelle des éléments Todo et dispatch sera le réducteur utilisé pour modifier la liste des éléments. Dans l'instruction return nous créons un groupe de divs pour chaque élément de notre tableau .

Notre application comportera désormais des erreurs car nous n'avons pas encore créé de fonction appelée . todoReducer . Ajoutons ce code juste en dessous de la ligne sur laquelle nous avons configuré notre affectation initialState .

 const todoReducer = (état, action) => {
  commutateur (action.type) {
    cas 'ADD_TODO': {
      return (action.name.length)
        ? [...state, {
          id: state.length ? Math.max(...state.map(todo => todo.id)) + 1 : 0,
          name: action.name,
          complete: false
        }]
        : Etat;
    }
    défaut: {
      état de retour;
    };
  }
}

Cela peut sembler complexe au début. Tout ce que cela fait est de mettre en place une fonction qui prend les actions et . Nous allumons ensuite cette action [TYPE]. Au début, nous n’aurons qu’une action, mais nous voulons également configurer une capture par défaut. Cette valeur par défaut retournera simplement l'état actuel.

Mais s'il détecte un ADD_TODO réel, nous renverrons l'état actuel, réparti, avec notre charge utile ajoutée à la fin. La partie délicate consiste à attribuer le nouvel identifiant. Ce que nous avons fait ici est pris la liste existante de Todos et renvoie l'identifiant maximal plus un, sinon 0.

Depuis que j'ai déjà configuré un initialState nous sommes prêts à passer à l'étape suivante. Nous devons nous assurer que lorsque vous tapez dans le champ de saisie, lorsque vous appuyez sur entrée, la valeur saisie est envoyée à une fonction qui effectuera la réduction.

Commençons donc par remplacer le div par le nom de classe todo-input par ce qui suit:

  
  
       

Cela garantit que lorsque nous tapons sur entrer, nous envoyons les informations du formulaire à une fonction appelée addTodo () . Nous référençons également l'entrée à l'aide de l'attribut ref et donnons à cet élément une valeur de référence de inputRef . Faire ces changements signifie que nous devons maintenant faire deux choses supplémentaires.

1) Nous devons créer une propriété appelée inputRef qui appelle le crochet useRef .
2) Nous devons créer une fonction appelée addTodo () .

Commençons par créer la propriété inputRef . En haut de votre composant Todo, ajoutez la propriété suivante:

const inputRef = useRef ();

Nous allons utiliser l'attribut ref pour obtenir une référence à l'entrée, cela nous permettra pour accéder à sa valeur plus tard. Cette référence sera appuyée par une propriété locale de notre composant fonctionnel Todo, mais il s'agira simplement d'un appel au crochet useRef qui nous permet d'accéder à toute la qualité de référence à l'intérieur d'un composant fonctionnel. Avec une propriété locale nommée inputRef vous pourrez effectuer des appels du type: inputRef.value .

Vous devrez également importer ce crochet comme nous l'avons fait avec . useReducer . Mettez à jour la première ligne du fichier index.js pour refléter ce qui suit:

import Réagissez, {useReducer, {useRef} from 'react';

Enfin, nous avons besoin de créez la fonction addTodo () qui utilisera cette référence et sera responsable de l'envoi de notre action de type ADD_TODO . Juste au-dessus de la déclaration ajoutez la fonction suivante:

 addTodo (event) {
  event.preventDefault ();
  envoi({
    type: 'ADD_TODO',
    nom: inputRef.current.value,
    complet: faux
  });
    inputRef.current.value = '';
} 

Dans notre fonction, nous appelons d'abord preventDefault () afin d'empêcher l'actualisation de la page lorsque nous cliquons sur le formulaire.

Nous envoyons ensuite notre ADD_TODO . action en utilisant inputRef pour accéder à la valeur en entrée à partir du formulaire. Tous les Todos obtiennent initialement une valeur complète de false. Enfin, nous définissons la valeur inputRef sur rien. Ceci efface le champ de saisie. Assez bien pour une démonstration.

Enfin, nous avons une dernière mise à jour à faire avant que le ADD_TODO ne fonctionne. À l’intérieur de notre JSX, nous cartographions encore l’état initial . Nous devons changer cela de:

{initialState.map ((todo) => (19459010)

:

{todos.map ((todo) => (

Nous devrions maintenant avoir un crochet de travail useReducer qui utilise notre fonction addTodo afin d'envoyer notre action au to toRedReducer .

Ajouter Todos terminés

Intégrons un crochet familier de notre premier article de blog: useEffect . Je veux simplement m'assurer que nous avons un exemple concret de ce crochet dans ce projet: Eh bien, nous allons donc mettre à jour le document.title chaque fois que nous cochons un Todo pour afficher le nombre ou le nombre de Todos terminés dans notre liste.

Juste au-dessus de notre addTodo () ajoutons la logique permettant de déterminer le nombre de Todos terminés, puis nous aurons besoin d’une méthode useEffect pour mettre à jour le document document.title wh fr il change:

 const completedTodos = todos.filter (todo => todo.complete);
useEffect (() => {
  // inputRef.current.focus ();
  document.title = `Vous avez les éléments $ {completedTodos.length} terminés!`;
})

Pour ce faire, nous devons également intégrer ce crochet:

import React, {useReducer, useRef, useEffect} de 'react';

Nous n'avons pas encore terminé - nous devons maintenant ajouter un événement qui appellera la fonction qui enverra notre COMPLETED_TODO . Ajoutez un gestionnaire onClick à notre div avec le className de todo-name .

toggleComplete (todo.id)}>   {todo.name}

Nous avons ensuite besoin d'une fonction pour gérer cet événement de clic. C'est simple et ne distribue qu'un identifiant simple et le type d'action. Ajoutez ceci juste en dessous de notre fonction addTodo () :

 toggleComplete (id) {
  dispatch ({type: 'TOGGLE_COMPLETE', id});
} 

Enfin, nous ajoutons le cas à notre todoReducer :

 cas 'TOGGLE_COMPLETE': {
  return state.map ((item) =>
    item.id === action.id
      ? {... item, complete:! item.complete}
      : article
  )
} 

J'ai également configuré un style et nous ajouterons ou supprimerons ce style en fonction de la valeur true de la tâche à compléter. Juste en dessous du code todos.map changeons la ligne de code qui ressemble à ceci:

  

à ceci:

  

Nous n'avons plus besoin de cet attribut alt, nous l'avons donc supprimé. Ça devrait le faire! Maintenant, lorsque nous cliquons sur notre Todos, une action est envoyée et la valeur complétée est définie sur true pour ce Todo spécifique. Notre filtre en tient compte par la méthode useEffect qui met à jour le document.title . Nous obtiendrons également notre nom de classe terminé et notre Todo terminé deviendra opaque pour représenter un Todo terminé.

À ce stade, tout fonctionne, à l'exception de la fonctionnalité de suppression, ainsi que du bouton cela devrait effacer tous les Todos de la liste. Pour compléter notre démonstration, nous allons répéter ce que nous avons déjà appris pour faire fonctionner ces deux dernières fonctionnalités.

Suppression d’un Todo

Il devrait être assez trivial à ce stade de raccorder (jeu de mots) une supprimer et effacer le bouton todos. Le style et le HTML ont déjà été pris en charge, il ne reste donc plus qu'à les faire fonctionner.

Commençons par ajouter l'événement onClick () pour l'icône de fermeture dans les todos HTML:

. deleteTodo (todo.id)}>   &fois;

Nous allons ajouter la fonction qui enverra l'action. Celles-ci ne doivent pas nécessairement être leur propre fonction, nous pourrions envoyer dès la onClick () ou configurer un fichier similaire. instruction switch pour gérer l’ensemble de la répartition. Nous pouvons adopter l’approche que nous souhaitons. Je voulais les ajouter un par un pour les besoins de cette démonstration.

Créons maintenant une fonction qui gérera la répartition:

 function deleteTodo (id) {
  dispatch ({type: 'DELETE_TODO', id});
} 

Et vous l'avez deviné, il nous suffit maintenant d'ajouter un cas dans notre instruction reducers switch pour gérer la réduction. C'est ici que nous supprimons un Todo de la liste et renvoyons l'ancien état moins la suppression. Nous pouvons le faire facilement parce que nous avons fourni un id . De la même manière, nous savons quel Todo terminer en passant un id nous pouvons également supprimer un élément, nous devons simplement nous assurer qu'il renvoie le nouvel état. Nous pouvons y parvenir avec le prototype de filtre multidirectionnel.

 cas 'DELETE_TODO': {
  return state.filter ((x) => x.id! == action.id);
}

Clearing All Todos

Pour le nettoyage des todos, nous n'avons pas besoin de beaucoup. Lorsque l'action est envoyée pour le CLEAR_TODOS la seule chose transmise est le type d'action réel, pas de charge utile, car une fois entré dans le réducteur, nous allons simplement retourner un tableau vide.

Voici les trois morceaux de code nécessaires pour y parvenir:

Ajoutez un onClick () au bouton HTML:

 onClick = {() => clearTodos ( )}

Ajoutez une fonction pour gérer l'envoi:

 function clearTodos () {
  dispatch ({type: 'CLEAR_TODOS'});
} 

Et un cas dans notre fonction de réduction:

 le cas 'CLEAR_TODOS': {
  retour [];
} 

Conclusion

Nous avons maintenant défini les bases d’une application Todo. Nous avons commencé avec la structure de base de la liste Todo et comment répéter simplement notre liste pour chaque Todo. Nous avons finalement créé toute la logique pour gérer l'état de la liste en utilisant une approche de gestion d'état basée sur le style Redux. Nous avons couvert l'ajout d'un nouveau Todo, la suppression, l'achèvement d'un Todo et enfin la possibilité de réinitialiser toute la liste de Todo (la partie la plus facile à compléter du réducteur).

Recherchez d'autres articles de notre part qui continueront notre cheminement vers Hooks. et les concepts avancés de Redux. Jusqu'à présent, nous avons couvert les bases et prévoyons de vous montrer comment continuer à tirer parti de toutes les merveilleuses façons dont vous pouvez utiliser les crochets React et d'autres fonctionnalités à la pointe de la technologie.

J'espère que cela vous a aidé à comprendre la bases de l'utilisation de React Hooks pour les réducteurs. Si vous êtes nouveau dans React, le blog de Telerik contient plus d'informations, plus précisément autour de All Things React qui contient une pléthore d'informations sur React et son écosystème. Veuillez explorer nos articles et produits et laissez-moi savoir si vous avez des questions ou des idées d'articles sur des sujets liés à React.


Les commentaires sont désactivés en mode aperçu.




Source link