Fermer

novembre 12, 2021

Crochets React utiles que vous pouvez utiliser dans vos projets


Résumé rapide ↬

Les composants basés sur les classes React sont désordonnés, déroutants, difficiles pour les humains et les machines. Mais avant React 16.8, les composants basés sur les classes étaient obligatoires pour tous les projets nécessitant des états, des méthodes de cycle de vie et de nombreuses autres fonctionnalités importantes. Tout cela a changé avec l'introduction des crochets dans React 16.8. Les crochets changent la donne. Ils ont simplifié React, l'ont rendu plus propre, plus facile à écrire et à déboguer, et ont également réduit la courbe d'apprentissage.

Les crochets sont simplement des fonctions qui vous permettent de vous accrocher à ou d'utiliser les fonctionnalités de React. Ils ont été introduits lors de la React Conf 2018 pour résoudre trois problèmes majeurs des composants de classe : l'enfer du wrapper, les composants énormes et les classes déroutantes. Les crochets donnent du pouvoir aux composants fonctionnels de React, ce qui permet de développer une application entière avec lui. Heureusement, les crochets ont résolu tous les problèmes de manière simple et efficace tout en créant de la place pour des fonctionnalités plus intéressantes dans React. Les hooks ne remplacent pas les concepts et les classes React déjà existants, ils fournissent simplement une API pour y accéder directement.

L'équipe React a introduit plusieurs hooks dans React 16.8. Cependant, vous pouvez également utiliser des crochets de fournisseurs tiers dans votre application ou même créer un crochet personnalisé. Dans ce tutoriel, nous examinerons quelques crochets utiles dans React et comment les utiliser. Nous passerons en revue plusieurs exemples de code de chaque hook et découvrirons également comment créer un hook personnalisé.

Remarque : Ce didacticiel nécessite une compréhension de base de Javascript (ES6+) et de React.[19659007]Plus après le saut ! Continuez à lire ci-dessous ↓

Motivation derrière les crochets

Comme indiqué précédemment , des hooks ont été créés pour résoudre trois problèmes : l'enfer du wrapper, des composants énormes et des classes déroutantes. Examinons chacun d'eux plus en détail.

Wrapper Hell

Les applications complexes construites avec des composants de classe s'exécutent facilement dans l'enfer du wrapper. Si vous examinez l'application dans React Dev Tools, vous remarquerez des composants profondément imbriqués. Cela rend très difficile le travail avec les composants ou leur débogage. Bien que ces problèmes puissent être résolus avec des composants d'ordre supérieur et render propsils vous obligent à modifier un peu votre code. Cela pourrait prêter à confusion dans une application complexe.

Les hooks sont faciles à partager, vous n'avez pas besoin de modifier vos composants avant de réutiliser la logique.

Un bon exemple est l'utilisation du Redux connect Higher Order Component (HOC) pour s'abonner au magasin Redux. Comme tous les HOC, pour utiliser le HOC de connexion, vous devez exporter le composant avec les fonctions d'ordre supérieur définies. Dans le cas de connectnous aurons quelque chose de cette forme.

export default connect(mapStateToProps, mapDispatchToProps)(MyComponent)

Where mapStateToProps et mapDispatchToProps sont des fonctions à définir.

Alors qu'à l'époque des Hooks, on peut facilement obtenir le même résultat de manière nette et succincte en utilisant les hooks Redux useSelector et useDispatch.

Composants énormes

Les composants de classe contiennent généralement des effets secondaires et une logique avec état. Au fur et à mesure que l'application devient de plus en plus complexe, il est courant que le composant devienne désordonné et déroutant. En effet, les effets secondaires devraient être organisés par méthodes de cycle de vie plutôt que par fonctionnalité. Bien qu'il soit possible de diviser les composants et de les simplifier, cela introduit souvent un niveau d'abstraction plus élevé.

Les crochets organisent les effets secondaires par fonctionnalité et il est possible de diviser un composant en morceaux en fonction de la fonctionnalité. Classes

Les classes sont généralement un concept plus difficile que les fonctions. Les composants basés sur les classes React sont verbeux et un peu difficiles pour les débutants. Si vous débutez avec Javascript, vous pourriez trouver des fonctions plus faciles à utiliser en raison de leur syntaxe légère par rapport aux classes. La syntaxe peut être déroutante ; parfois, il est possible d'oublier de lier un gestionnaire d'événements qui pourrait casser le code.

React résout ce problème avec des composants fonctionnels et des crochets, permettant aux développeurs de se concentrer sur le projet plutôt que sur la syntaxe du code.

Par exemple, ce qui suit deux composants React donneront exactement le même résultat.

import React, { Component } from "react";
exporter la classe par défaut L'application étend le composant {
  constructeur (accessoires) {
    super(accessoires);
    this.état = {
      nombre : 0
    } ;
    this.incrementNumber = this.incrementNumber.bind(this);
  }
  incrémentNumber() {
    this.setState({ num : this.state.num + 1 });
  }
  rendu() {
    revenir (
      

{this.state.num}

); } }
import React, { useState } de "react" ;
exporter la fonction par défaut App() {
  const [num, setNum] = useState(0);
  fonction incrémentNombre() {
    setNum(num + 1);
  }
  revenir (
    

{num}

); }

Le premier exemple est un composant basé sur les classes tandis que le second est un composant fonctionnel. Bien qu'il s'agisse d'un exemple simple, notez à quel point le premier exemple est faux par rapport au second. qui s'appliquent à eux. Voici quelques-unes des règles qui s'appliquent aux crochets.

  1. La convention de dénomination des crochets doit commencer par le préfixe use. Ainsi, nous pouvons avoir useStateuseEffectetc. Si vous utilisez des éditeurs de code modernes comme Atom et VSCode, le plugin ESLint pourrait être une fonctionnalité très utile pour les hooks React. Le plugin fournit des avertissements et des conseils utiles sur les meilleures pratiques.
  2. Les hooks doivent être appelés au niveau supérieur d'un composant, avant l'instruction return. Ils ne peuvent pas être appelés à l'intérieur d'une instruction conditionnelle, d'une boucle ou de fonctions imbriquées.
  3. Les hooks doivent être appelés à partir d'une fonction React (à l'intérieur d'un composant React ou d'un autre hook). Il ne doit pas être appelé à partir d'une fonction Vanilla JS. Comme les autres hooks intégrés, ce hook doit être importé de react pour être utilisé dans notre application.

    import {useState} from 'react'

    Pour initialiser l'état, nous devons déclarer les deux l'état et sa fonction de mise à jour et passer une valeur initiale.

    const [state, updaterFn] = useState('')

    Nous sommes libres d'appeler notre fonction d'état et de mise à jour comme bon nous semble mais par convention, le premier élément de la array sera notre état tandis que le deuxième élément sera la fonction de mise à jour. C'est une pratique courante de préfixer notre fonction de mise à jour avec le préfixe set suivi du nom de notre état sous forme de cas de chameau.

    Par exemple, définissons un état pour contenir les valeurs de comptage.

     const [count, setCount] = useState(0)

    Remarquez que la valeur initiale de notre état count est définie sur 0 et non sur une chaîne vide. En d'autres termes, nous pouvons initialiser notre état sur n'importe quel type de variables JavaScript, à savoir nombre, chaîne, booléen, tableau, objet et même BigInt. Il existe une nette différence entre la définition d'états avec le hook useState et les états de composants basés sur les classes. Il est à noter que le hook useState renvoie un tableau, également connu sous le nom de variables d'état et dans l'exemple ci-dessus, nous avons déstructuré le tableau en state et la fonction updater .

    Rendu des composants

    La définition des états avec le hook useState provoque le réaffichage du composant correspondant. Cependant, cela ne se produit que si React détecte une différence entre l'état précédent ou ancien et le nouvel état. React effectue la comparaison des états à l'aide de l'algorithme Javascript Object.is .

    Setting States With useState

    Notre état count peut être défini aux nouvelles valeurs d'état en passant simplement la nouvelle valeur à la fonction de mise à jour setCount comme suit setCount(newValue).

    Cette méthode fonctionne lorsque nous ne voulons pas référencer la précédente valeur de l'état. Si nous souhaitons faire cela, nous devons passer une fonction à la fonction setCount.

    En supposant que nous voulions ajouter 5 à notre variable count chaque fois qu'un bouton est cliqué, nous pourrait faire ce qui suit.

    importer {useState} depuis 'react'
    
    const CountExample = () => {
      // initialise notre état de comptage
      const [count, setCount] = useState(0)
      
      // ajoute 5 à l'état précédent du compte
      const handleClick = () =>{
        setCount(prevCount => prevCount + 5)
      }
      revenir(
        

    {count}

    ) } export default CountExample

    Dans le code ci-dessus, nous avons d'abord importé le hook useState de reactpuis initialisé l'état count avec une valeur par défaut de 0. Nous avons créé un gestionnaire onClick pour incrémenter la valeur de count de 5 chaque fois que le bouton est cliqué. Ensuite, nous avons affiché le résultat dans une balise h1.

    Définition des tableaux et des états des objets

    Les états des tableaux et des objets peuvent être définis de la même manière que les autres types de données. Cependant, si nous souhaitons conserver les valeurs déjà existantes, nous devons utiliser l'opérateur de propagation ES6 lors de la définition des états.

    L'opérateur de propagation en Javascript est utilisé pour créer un nouvel objet à partir d'un objet déjà existant. Ceci est utile ici car React compare les états avec l'opération Object.ispuis effectue un nouveau rendu en conséquence.

    Considérons le code ci-dessous pour définir les états lors d'un clic sur un bouton.

     importer {useState} depuis 'react'
    
    const StateExample = () => {
      //initialiser nos états de tableau et d'objet
      const [arr, setArr] = useState([2, 4])
      const [obj, setObj] = useState({num : 1, nom : 'Desmond'})
      
      // définit arr sur les nouvelles valeurs du tableau
      const handleArrClick = () =>{
        const newArr = [1, 5, 7]
        setArr([...arr, ...newArr])
      }
      
      // définit obj sur les nouvelles valeurs de l'objet
      const handleObjClick = () =>{
        const newObj = {nom : 'Ifeanyi', âge : 25}
        setObj({...obj, ...newObj})
      }
    
      revenir(
        
    ) } export default StateExample

    Dans le code ci-dessus, nous avons créé deux états arr et objet les avons initialisés à des valeurs de tableau et d'objet respectivement. Nous avons ensuite créé des gestionnaires onClick appelés handleArrClick et handleObjClick pour définir respectivement les états du tableau et de l'objet. Lorsque handleArrClick se déclenche, nous appelons setArr et utilisons l'opérateur de propagation ES6 pour répartir les valeurs de tableau déjà existantes et y ajouter newArr.

    Nous avons fait de même. chose pour le gestionnaire handleObjClick. Ici, nous avons appelé setObjdiffusé les valeurs d'objet existantes à l'aide de l'opérateur de diffusion ES6 et mis à jour les valeurs de name et age.

    Async Nature Of useState

    Comme nous l'avons déjà vu, nous définissons les états avec useState en passant une nouvelle valeur à la fonction de mise à jour. Si le programme de mise à jour est appelé plusieurs fois, les nouvelles valeurs seront ajoutées à une file d'attente et le nouveau rendu est effectué en conséquence à l'aide de la comparaison JavaScript Object.is.

    Les états sont mis à jour de manière asynchrone. Cela signifie que le nouvel état est d'abord ajouté à un état en attente et par la suite, l'état est mis à jour. Ainsi, vous pouvez toujours obtenir l'ancienne valeur d'état si vous accédez à l'état dès qu'il est défini.

    Considérons l'exemple suivant pour observer ce comportement.

    Dans le code ci-dessus, nous avons créé un count à l'aide du hook useState. Nous avons ensuite créé un gestionnaire onClick pour incrémenter l'état count chaque fois que le bouton est cliqué.
    Notez que bien que l'état count ait augmenté, comme indiqué dans la balise h2l'état précédent est toujours enregistré dans la console. Cela est dû à la nature asynchrone du hook.

    Si nous souhaitons obtenir le nouvel état, nous pouvons le gérer de la même manière que nous gérerions les fonctions asynchrones. Voici une façon de le faire.

    Ici, nous avons stocké newCountValue créé pour stocker la valeur de comptage mise à jour, puis défini l'état count avec la valeur mise à jour. Ensuite, nous avons enregistré la valeur de comptage mise à jour dans la console.

    Le useEffect Hook

    useEffect est un autre hook React important utilisé dans la plupart des projets. Il fait la même chose que les méthodes de cycle de vie componentDidMountcomponentWillUnmount et componentDidUpdate du composant basé sur les classes. useEffect nous offre la possibilité d'écrire des codes impératifs qui peuvent avoir des effets secondaires sur l'application. Des exemples de tels effets incluent la journalisation, les abonnements, les mutations, etc.

    L'utilisateur peut décider quand useEffect s'exécutera, cependant, s'il n'est pas défini, les effets secondaires s'exécuteront à chaque rendu ou nouveau rendu .

    Considérez l'exemple ci-dessous.

    import {useState, useEffect} depuis 'react'
    
    const App = () =>{
      const [count, setCount] = useState(0)
      useEffect(() =>{
        console.log (nombre)
      })
    
      revenir(
        
    ...
    ) }

    Dans le code ci-dessus, nous avons simplement enregistré count dans le useEffect. Cela s'exécutera après chaque rendu du composant.

    Parfois, nous pouvons vouloir exécuter le hook une fois (sur le montage) dans notre composant. Nous pouvons y parvenir en fournissant un deuxième paramètre au crochet useEffect.

    import {useState, useEffect} à partir de 'react'
    
    const App = () =>{
      const [count, setCount] = useState(0)
      useEffect(() =>{
        setCount(count + 1)
      }, [])
    
      revenir(
        

    {count}

    ...
    ) }

    Le hook useEffect a deux paramètres, le premier paramètre est la fonction que nous voulons exécuter tandis que le deuxième paramètre est un tableau de dépendances. Si le deuxième paramètre n'est pas fourni, le hook s'exécutera en continu.

    En passant un crochet vide au deuxième paramètre du hook, nous demandons à React d'exécuter le hook useEffect une seule fois, sur le support. Cela affichera la valeur 1 dans la balise h1 car le nombre sera mis à jour une fois, de 0 à 1, lors du montage du composant.

    Nous pourrions également faire notre effet secondaire. exécuter chaque fois que certaines valeurs dépendantes changent. Cela peut être fait en passant ces valeurs dans la liste des dépendances.

    Par exemple, nous pourrions faire en sorte que useEffect s'exécute chaque fois que count change comme suit.

    import { useState, useEffect } de "react" ;
    const App = () => {
      const [count, setCount] = useState(0);
      useEffect(() => {
        console.log(compte);
      }, [count]);
      revenir (
        
    ); } ; export default App;

    Le useEffect ci-dessus s'exécutera lorsque l'une de ces deux conditions est remplie.

    1. Au montage — après le rendu du composant.
    2. Lorsque la valeur de compte change.

    Au montage, l'expression console.log s'exécutera et consignera count à 0. Une fois le count mis à jour, le deuxième la condition est remplie, donc le useEffect s'exécute à nouveau, cela continuera chaque fois que le bouton est cliqué.

    Une fois que nous avons fourni le deuxième argument à useEffectil est prévu que nous passons tout les dépendances à celui-ci. Si vous avez installé ESLINTune erreur de lint s'affichera si aucune dépendance n'est transmise à la liste de paramètres. Cela pourrait également faire en sorte que l'effet secondaire se comporte de manière inattendue, surtout si cela dépend des paramètres qui ne sont pas transmis.

    Cleaning Up The Effect

    useEffect nous permet également de nettoyer les ressources avant le démontage du composant . Cela peut être nécessaire pour éviter les fuites de mémoire et rendre l'application plus efficace. Pour ce faire, nous renvoyons la fonction de nettoyage à la fin du hook.

    useEffect(() => {
      console.log('monté')
    
      return () => console.log('démontage... nettoyer ici')
    })

    Le crochet useEffect ci-dessus enregistrera mount lorsque le composant est monté. Démontage… nettoyer ici sera consigné lors du démontage du composant. Cela peut se produire lorsque le composant est supprimé de l'interface utilisateur.

    Le processus de nettoyage suit généralement le formulaire ci-dessous.

    useEffect(() => {
      //L'effet que nous avons l'intention de faire
      effet
      
      //Nous retournons ensuite le nettoyage
      return() => le nettoyage/désabonnement
    })

    Bien que vous ne trouviez pas autant de cas d'utilisation pour les abonnements useEffectil est utile pour les abonnements et les minuteurs. En particulier, lorsqu'il s'agit de sockets Web, vous devrez peut-être vous désabonner du réseau pour économiser des ressources et améliorer les performances lors du démontage du composant.

    Récupération et récupération de données avec useEffect

    L'une des utilisations les plus courantes les cas du crochet useEffect récupèrent et prélèvent des données à partir d'une API.

    Pour illustrer cela, nous utiliserons de fausses données utilisateur que j'ai créées à partir de JSONPlaceholder pour récupérer des données avec le JSONPlaceholder hook useEffect.

    import { useEffect, useState } à partir de "react" ;
    importer des axios depuis "axios" ;
    
    exporter la fonction par défaut App() {
      const [users, setUsers] = useState([]);
      const endPoint =
        "https://my-json-server.typicode.com/ifeanyidike/jsondata/users" ;
    
      useEffect(() => {
        const fetchUsers = async () => {
          const { data } = wait axios.get(endPoint);
          setUsers(données);
        } ;
        fetchUsers();
      }, []);
    
      revenir (
        
    {users.map((utilisateur) => (

    {user.name}

    Occupation : {user.job}

    Sexe : {user.sex}

    ))}
    ); }

    Dans le code ci-dessus, nous avons créé un état users à l'aide du hook useState. Ensuite, nous avons récupéré les données d'une API à l'aide d'Axios. Il s'agit d'un processus asynchrone, et nous avons donc utilisé la fonction async/await, nous aurions également pu utiliser le point puis la syntaxe. Puisque nous avons récupéré une liste d'utilisateurs, nous l'avons simplement mappée pour afficher les données.

    Notez que nous avons passé un paramètre vide au hook. Cela garantit qu'il n'est appelé qu'une seule fois lors du montage du composant.

    Nous pouvons également récupérer les données lorsque certaines conditions changent. Nous allons le montrer dans le code ci-dessous.

    import { useEffect, useState } from "react" ;
    importer des axios depuis "axios" ;
    
    exporter la fonction par défaut App() {
      const [userIDs, setUserIDs] = useState([]);
      const [user, setUser] = useState({});
      const [currentID, setCurrentID] = useState(1);
    
      const endPoint =
        "https://my-json-server.typicode.com/ifeanyidike/userdata/users" ;
    
      useEffect(() => {
        axios.get(endPoint).then(({ data }) => setUserIDs(data));
      }, []);
    
      useEffect(() => {
        const fetchUserIDs = async () => {
          const { data } = wait axios.get(`${endPoint}/${currentID}`});
          setUser(données);
        } ;
    
        fetchUserIDs();
      }, [currentID]);
    
      const moveToNextUser = () => {
        setCurrentID((prevId) => (prevId < userIDs.length ? prevId + 1 : prevId));
      };
      const moveToPrevUser = () => {
        setCurrentID((prevId) => (prevId === 1 ? prevId : prevId - 1));
      } ;
      revenir (
        

    {user.name}

    Occupation : {user.job}

    Sexe : {user.sex}

    ); }

    Ici, nous avons créé deux hooks useEffect. Dans le premier, nous avons utilisé la syntaxe point puis pour obtenir tous les utilisateurs de notre API. Cela est nécessaire pour déterminer le nombre d'utilisateurs.

    Nous avons ensuite créé un autre hook useEffect pour obtenir un utilisateur basé sur le id. Ce useEffect récupèrera les données chaque fois que l'identifiant changera. Pour garantir cela, nous avons passé l'id dans la liste des dépendances.

    Ensuite, nous avons créé des fonctions pour mettre à jour la valeur de notre id chaque fois que les boutons sont cliqués. Une fois la valeur de id modifiée, useEffect s'exécutera à nouveau et récupèrera les données.

    Si nous le souhaitons, nous pouvons même nettoyer ou annuler le jeton basé sur la promesse dans Axios, nous pourrions le faire avec la méthode de nettoyage décrite ci-dessus.

    useEffect(() => {
        const source = axios.CancelToken.source();
        const fetchUsers = async () => {
          const { data } = wait axios.get(`${endPoint}/${num}`, {
            cancelToken : source.token
          });
          setUser(données);
        } ;
        fetchUsers();
    
        return() => source.cancel();
      }, [num]);

    Ici, nous avons passé le jeton Axios comme deuxième paramètre à axios.get. Lorsque le composant est démonté, nous avons ensuite annulé l'abonnement en appelant la méthode cancel de l'objet source.

    Le crochet useReducer

    Le crochet useReducer est un crochet React très utile qui fait une chose similaire au crochet useState. Selon la documentation Reactce hook doit être utilisé pour gérer une logique plus complexe que le hook useState. Il convient de noter que le hook useState est implémenté en interne avec le hook useReducer.

    Le hook prend un réducteur comme argument et peut éventuellement prendre l'état initial et une fonction init comme arguments.

     const [state, dispatch] = useReducer(reducer, initialState, init)

    Ici, init est une fonction et elle est utilisée chaque fois que nous voulons créer l'état initial paresseusement.

    Regardons comment implémentez le hook useReducer en créant une simple application à faire comme indiqué dans le bac à sable ci-dessous.

    Exemple de Todo

    Tout d'abord, nous devons créer notre réducteur pour contenir les états.

    export. const ADD_TODO = "ADD_TODO";
    export const REMOVE_TODO = "REMOVE_TODO";
    export const COMPLETE_TODO = "COMPLETE_TODO";
    
    réducteur const = (état, action) => {
      commutateur (action.type) {
        cas ADD_TODO :
          const newTodo = {
            id : action.id,
            texte : action.texte,
            terminé : faux
          } ;
          retour [...state, newTodo];
        cas REMOVE_TODO :
          return state.filter((todo) => todo.id !== action.id);
        cas COMPLETE_TODO :
          const completeTodo = state.map((todo) => {
            si (todo.id === action.id) {
              revenir {
                ...à faire,
                terminé : !todo.completed
              } ;
            } autre {
              retourner à faire;
            }
          });
          retourner completeTodo;
        défaut:
          état de retour ;
      }
    } ;
    réducteur par défaut d'exportation ;

    Nous avons créé trois constantes correspondant à nos types d'action. Nous aurions pu utiliser des chaînes directement mais cette méthode est préférable pour éviter les fautes de frappe.

    Ensuite, nous avons créé notre fonction de réduction. Comme dans Reduxle réducteur doit prendre l'état et l'objet d'action. Mais contrairement à Redux, nous n'avons pas besoin d'initialiser notre réducteur ici. le contexte peut permettre à une application plus importante de déclencher des actions, de mettre à jour state et de l'écouter.

    Ensuite, nous avons utilisé les instructions switch pour vérifier le type d'action transmis par l'utilisateur. Si le type d'action est ADD_TODOon veut passer une nouvelle to-do et si c'est REMOVE_TODOon veut filtrer les to-dos et supprimer celle qui correspond au id transmis par l'utilisateur. S'il s'agit de COMPLETE_TODOnous souhaitons mapper les tâches à effectuer et basculer celle avec l'id transmis par l'utilisateur.

    Voici le App.js dans lequel nous avons implémenté le reducer.

    import { useReducer, useState } à partir de "react" ;
    importer "./styles.css" ;
    réducteur d'importation, { ADD_TODO, REMOVE_TODO, COMPLETE_TODO } de "./reducer" ;
    exporter la fonction par défaut App() {
      const [id, setId] = useState(0);
      const [text, setText] = useState("");
      const initialState = [
        {
          id: id,
          text: "First Item",
          completed: false
        }
      ];
    
      //On pourrait aussi passer un tableau vide comme état initial
      //const initialState = []
      
      const [state, dispatch] = useReducer(reducer, initialState);
      const addTodoItem = (e) => {
        e.preventDefault();
        const newId = id + 1;
        setId(newId);
        envoi({
          tapez : ADD_TODO,
          id : nouvelId,
          texte : texte
        });
        Définir le texte("");
      } ;
      const removeTodo = (id) => {
        dispatch({ type : REMOVE_TODO, id });
      } ;
      const completeTodo = (id) => {
        dispatch({ type : COMPLET_TODO, id });
      } ;
      revenir (
        

    Exemple à faire

    setText(e.target.value)} />
    {state.map((todo) => (

    {todo.text}

    removeTodo(todo.id)}>✕ completeTodo(todo.id)}>✓
    ))}
    ); }

    Ici, nous avons créé un formulaire contenant un élément input, pour collecter l'entrée de l'utilisateur, et un bouton pour déclencher l'action. Lorsque le formulaire est soumis, nous avons envoyé une action de type ADD_TODOen passant un nouvel identifiant et un texte à faire. Nous avons créé un nouvel identifiant en incrémentant la valeur de l'identifiant précédent de 1. Nous avons ensuite effacé la zone de texte de saisie. Pour supprimer et terminer la tâche, nous avons simplement envoyé les actions appropriées. Ceux-ci ont déjà été implémentés dans le réducteur comme indiqué ci-dessus.

    Cependant, la magie opère car nous utilisons le crochet useReducer. Ce hook accepte le réducteur et l'état initial et renvoie l'état et la fonction de répartition. Ici, la fonction dispatch a le même objectif que la fonction setter pour le hook useState et nous pouvons l'appeler comme nous voulons au lieu de dispatch.

    Pour afficher le to-do éléments, nous avons simplement mappé la liste des tâches renvoyées dans notre objet d'état, comme indiqué dans le code ci-dessus.

    Cela montre la puissance du crochet useReducer. Nous pourrions également obtenir cette fonctionnalité avec le hook useState mais comme vous pouvez le voir dans l'exemple ci-dessus, le hook useReducer nous a aidé à garder les choses plus propres. useReducer est souvent bénéfique lorsque l'objet d'état est une structure complexe et est mis à jour de différentes manières par rapport à un simple remplacement de valeur. De plus, une fois que ces fonctions de mise à jour deviennent plus compliquées, useReducer permet de conserver facilement toute cette complexité dans une fonction de réduction (qui est une fonction JS pure), ce qui facilite l'écriture de tests pour la seule fonction de réduction.

    Nous aurions également pu passer le troisième argument au hook useReducer pour créer l'état initial paresseusement. Cela signifie que nous pourrions calculer l'état initial dans une fonction init.

    Par exemple, nous pourrions créer une fonction init comme suit :

    const initFunc = () = > [
      {
          id: id,
          text: "First Item",
          completed: false
        }
    ]

    puis transmettez-le à notre hook useReducer.

    const [state, dispatch] = useReducer(reducer, initialState, initFunc)

    Si nous faisons cela, le initFunc remplacera le initialState que nous avons fourni et l'état initial sera calculé paresseusement.

    Le crochet useContext

    L'API React Context fournit un moyen de partager des états ou des données dans l'arborescence des composants React. L'API est disponible dans React, en tant que fonctionnalité expérimentale, depuis un certain temps, mais son utilisation est devenue sûre dans React 16.3.0. L'API facilite le partage de données entre les composants tout en éliminant le perçage des accessoires.

    Bien que vous puissiez appliquer le contexte React à l'ensemble de votre application, il est également possible de l'appliquer à une partie de l'application.

    Pour utiliser le crochet, vous devez d'abord créer un contexte à l'aide de React.createContext et ce contexte peut ensuite être transmis au hook.

    Pour démontrer l'utilisation du hook useContextcréons une application simple cela augmentera la taille de la police dans toute notre application.

    Créons notre contexte dans le fichier context.js.

    import { createContext } à partir de "react" ;
    
    //Ici, nous définissons le fontSize initial sur 16.
    const fontSizeContext = createContext(16);
    export default fontSizeContext;

    Ici, nous avons créé un contexte et lui avons passé une valeur initiale de 16puis nous avons exporté le contexte. Ensuite, connectons notre contexte à notre application.

    import FontSizeContext from "./context";
    importer { useState } de "react" ;
    importer PageOne à partir de "./PageOne" ;
    importer PageTwo à partir de "./PageTwo" ;
    const App = () => {
      const [size, setSize] = useState(16);
      revenir (
        
          
          
          
          
        
      );
    } ;
    export default App;

    Dans le code ci-dessus, nous avons enveloppé l'intégralité de notre arborescence de composants avec FontSizeContext.Provider et passé size à sa valeur prop. Ici, size est un état créé avec le hook useState. Cela nous permet de changer la valeur prop chaque fois que l'état size change. En enveloppant le composant entier avec le Providernous pouvons accéder au contexte n'importe où dans notre application.

    Par exemple, nous avons accédé au contexte dans et . En conséquence, la taille de la police augmentera dans ces deux composants lorsque nous l'augmentons à partir du fichier App.js. Nous pouvons augmenter ou diminuer la taille de la police à partir des boutons comme indiqué ci-dessus et une fois que nous le faisons, la taille de la police change dans toute l'application.

    import { useContext } from "react";
    importer le contexte de "./context" ;
    const PageOne = () => {
      taille const = useContext(context);
      renvoie 

    Contenu de la première page

     ; } ; export default PageOne;

    Ici, nous avons accédé au contexte à l'aide du hook useContext de notre composant PageOne. Nous avons ensuite utilisé ce contexte pour définir notre propriété font-size. Une procédure similaire s'applique au fichier PageTwo.js.

    Les thèmes ou d'autres configurations de niveau d'application d'ordre supérieur sont de bons candidats pour les contextes.

    Utilisation de useContext Et useReducer

    When used with the useReducer hook, useContext allows us to create our own state management system. We can create global states and easily manage them in our application.

    Let’s improve our to-do application using the context API.

    As usual, we need to create a todoContext in the todoContext.js file.

    import { createContext } from "react";
    const initialState = [];
    export default createContext(initialState);

    Here we created the context, passing an initial value of an empty array. Then we exported the context.

    Let’s refactor our App.js file by separating the to-do list and items.

    import { useReducer, useState } from "react";
    import "./styles.css";
    import todoReducer, { ADD_TODO } from "./todoReducer";
    import TodoContext from "./todoContext";
    import TodoList from "./TodoList";
    
    export default function App() {
      const [id, setId] = useState(0);
      const [text, setText] = useState("");
      const initialState = [];
      const [todoState, todoDispatch] = useReducer(todoReducer, initialState);
    
      const addTodoItem = (e) => {
        e.preventDefault();
        const newId = id + 1;
        setId(newId);
        todoDispatch({
          type: ADD_TODO,
          id: newId,
          text: text
        });
        setText("");
      };
      return (
        
            

    Todo Example

    setText(e.target.value)} />
    ); }

    Here, we wrapped our App.js file with the TodoContext.Provider then we passed the return values of our todoReducer to it. This makes the reducer’s state and dispatch function to be accessible throughout our application.

    We then separated the to-do display into a component TodoList. We did this without prop drilling, thanks to the Context API. Let’s take a look at the TodoList.js file.

    import React, { useContext } from "react";
    import TodoContext from "./todoContext";
    import Todo from "./Todo";
    const TodoList = () => {
      const [state] = useContext(TodoContext);
      return (
        
    {state.map((todo) => ( ))}
    ); }; export default TodoList;

    Using array destructuring, we can access the state (leaving the dispatch function) from the context using the useContext hook. We can then map through the state and display the to-do items. We still extracted this in a Todo component. The ES6+ map function requires us to pass a unique key and since we need the specific to-do, we pass it alongside as well.

    Let’s take a look at the Todo component.

    import React, { useContext } from "react";
    import TodoContext from "./todoContext";
    import { REMOVE_TODO, COMPLETE_TODO } from "./todoReducer";
    const Todo = ({ todo }) => {
      const [, dispatch] = useContext(TodoContext);
      const removeTodo = (id) => {
        dispatch({ type: REMOVE_TODO, id });
      };
      const completeTodo = (id) => {
        dispatch({ type: COMPLETE_TODO, id });
      };
      return (
        

    {todo.text}

    removeTodo(todo.id)}>✕ completeTodo(todo.id)}>✓
    ); }; export default Todo;

    Again using array destructuring, we accessed the dispatch function from the context. This allows us to define the completeTodo and removeTodo function as already discussed in the useReducer section. With the todo prop passed from todoList.js we can display a to-do item. We can also mark it as completed and remove the to-do as we deem fit.

    It is also possible to nest more than one context provider in the root of our application. This means that we can use more than one context to perform different functions in an application.

    To demonstrate this, let’s add theming to the to-do example.

    Here’s what we’ll be building.

    Again, we have to create themeContext. To do this, create a themeContext.js file and add the following codes.

    import { createContext } from "react";
    import colors from "./colors";
    export default createContext(colors.light);

    Here, we created a context and passed colors.light as the initial value. Let’s define the colors with this property in the colors.js file.

    const colors = {
      light: {
        backgroundColor: "#fff",
        color: "#000"
      },
      dark: {
        backgroundColor: "#000",
        color: "#fff"
      }
    };
    export default colors;

    In the code above, we created a colors object containing light and dark properties. Each property has backgroundColor and color object.

    Next, we create the themeReducer to handle the theme states.

    import Colors from "./colors";
    export const LIGHT = "LIGHT";
    export const DARK = "DARK";
    const themeReducer = (state, action) => {
      switch (action.type) {
        case LIGHT:
          return {
            ...Colors.light
          };
        case DARK:
          return {
            ...Colors.dark
          };
        default:
          return state;
      }
    };
    export default themeReducer;

    Like all reducers, the themeReducer takes the state and the action. It then uses the switch statement to determine the current action. If it’s of type LIGHTwe simply assign Colors.light props and if it’s of type DARKwe display Colors.dark props. We could have easily done this with the useState hook but we choose useReducer to drive the point home.

    Having set up the themeReducerwe can then integrate it in our App.js file.

    import { useReducer, useState, useCallback } from "react";
    import "./styles.css";
    import todoReducer, { ADD_TODO } from "./todoReducer";
    import TodoContext from "./todoContext";
    import ThemeContext from "./themeContext";
    import TodoList from "./TodoList";
    import themeReducer, { DARK, LIGHT } from "./themeReducer";
    import Colors from "./colors";
    import ThemeToggler from "./ThemeToggler";
    
    const themeSetter = useCallback(
          theme => themeDispatch({type: theme}, 
        [themeDispatch]);
    
    export default function App() {
      const [id, setId] = useState(0);
      const [text, setText] = useState("");
      const initialState = [];
      const [todoState, todoDispatch] = useReducer(todoReducer, initialState);
      const [themeState, themeDispatch] = useReducer(themeReducer, Colors.light);
      const themeSetter = useCallback(
        (theme) => {
          themeDispatch({ type: theme });
        },
        [themeDispatch]
      );
      const addTodoItem = (e) => {
        e.preventDefault();
        const newId = id + 1;
        setId(newId);
        todoDispatch({
          type: ADD_TODO,
          id: newId,
          text: text
        });
        setText("");
      };
    
      return (
        
          
            

    Todo Example

    setText(e.target.value)} />
    ); }

    In the above code, we added a few things to our already existing to-do application. We began by importing the ThemeContextthemeReducerThemeTogglerand Colors. We created a reducer using the useReducer hook, passing the themeReducer and an initial value of Colors.light to it. This returned the themeState and themeDispatch to us.

    We then nested our component with the provider function from the ThemeContextpassing the themeState and the dispatch functions to it. We also added theme styles to it by spreading out the themeStates. This works because the colors object already defined properties similar to what the JSX styles will accept.

    However, the actual theme toggling happens in the ThemeToggler component. Let’s take a look at it.

    import ThemeContext from "./themeContext";
    import { useContext, useState } from "react";
    import { DARK, LIGHT } from "./themeReducer";
    const ThemeToggler = () => {
      const [showLight, setShowLight] = useState(true);
      const [themeState, themeSetter] = useContext(ThemeContext);
      const dispatchDarkTheme = () => themeSetter(DARK);
      const dispatchLightTheme = () => themeSetter(LIGHT);
      const toggleTheme = () => {
        showLight ? dispatchDarkTheme() : dispatchLightTheme();
        setShowLight(!showLight);
      };
      console.log(themeState);
      return (
        
    ); }; export default ThemeToggler;

    In this component, we used the useContext hook to retrieve the values we passed to the ThemeContext.Provider from our App.js file. As shown above, these values include the ThemeStatedispatch function for the light theme, and dispatch function for the dark theme. Thereafter, we simply called the dispatch functions to toggle the themes. We also created a state showLight to determine the current theme. This allows us to easily change the button text depending on the current theme.

    The useMemo Hook

    The useMemo hook is designed to memoize expensive computations. Memoization simply means caching. It caches the computation result with respect to the dependency values so that when the same values are passed, useMemo will just spit out the already computed value without recomputing it again. This can significantly improve performance when done correctly.

    The hook can be used as follows:

    const memoizedResult = useMemo(() => expensiveComputation(a, b), [a, b])

    Let’s consider three cases of the useMemo hook.

    1. When the dependency values, a and b remain the same.
      The useMemo hook will return the already computed memoized value without recomputation.
    2. When the dependency values, a and b change.
      The hook will recompute the value.
    3. When no dependency value is passed.
      The hook will recompute the value.

    Let’s take a look at an example to demonstrate this concept.

    In the example below, we’ll be computing the PAYE and Income after PAYE of a company’s employees with fake data from JSONPlaceholder.

    The calculation will be based on the personal income tax calculation procedure for Nigeria providers by PricewaterhouseCoopers available here.

    This is shown in the sandbox below.

    First, we queried the API to get the employees’ data. We also get data for each employee (with respect to their employee id).

    const [employee, setEmployee] = useState({});
      const [employees, setEmployees] = useState([]);
      const [num, setNum] = useState(1);
      const endPoint =
        "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees";
      useEffect(() => {
        const getEmployee = async () => {
          const { data } = await axios.get(`${endPoint}/${num}`);
          setEmployee(data);
        };
        getEmployee();
      }, [num]);
      useEffect(() => {
        axios.get(endPoint).then(({ data }) => setEmployees(data));
      }, [num]);

    We used axios and the async/await method in the first useEffect and then the dot then syntax in the second. These two approaches work in the same way.

    Next, using the employee data we got from above, let’s calculate the relief variables:

    const taxVariablesCompute = useMemo(() => {
        const { income, noOfChildren, noOfDependentRelatives } = employee;
        
        //supposedly complex calculation
        //tax relief computations for relief Allowance, children relief, 
        // relatives relief and pension relief
    
        const reliefs =
          reliefAllowance1 +
          reliefAllowance2 +
          childrenRelief +
          relativesRelief +
          pensionRelief;
        return reliefs;
      }, [employee]);

    This is a fairly complex calculation and so we had to wrap it in a useMemo hook to memoize or optimize it. Memoizing it this way will ensure that the calculation will not be recomputed if we tried to access the same employee again.

    Furthermore, using the tax relief values obtained above, we’d like to calculate the PAYE and income after PAYE.

    const taxCalculation = useMemo(() => {
        const { income } = employee;
        let taxableIncome = income - taxVariablesCompute;
        let PAYE = 0;
        
        //supposedly complex calculation
        //computation to compute the PAYE based on the taxable income and tax endpoints
        
        const netIncome = income - PAYE;
        return { PAYE, netIncome };
      }, [employee, taxVariablesCompute]);

    We performed tax calculation (a fairly complex calculation) using the above-computed tax variables and then memoized it with the useMemo hook.

    The complete code is available on here.

    This follows the tax calculation procedure given here. We first computed the tax relief considering income, number of children, and number of dependent relatives. Then, we multiplied the taxable income by the PIT rates in steps. While the calculation in question is not entirely necessary for this tutorial, it is provided to show us why useMemo may be necessary. This is also a fairly complex calculation and so we may need to memorize it with useMemo as shown above.

    After calculating the values, we simply displayed the result.

    Note the following about the useMemo hook.

    • useMemo should be used only when it is necessary to optimize the computation. In other words, when recomputation is expensive.
    • It is advisable to first write the calculation without memorization and only memorize it if it is causing performance issues.
    • Unnecessary and irrelevant use of the useMemo hook may even compound the performance issues.
    • Sometimes, too much memoization can also cause performance issues.

    The useCallback Hook

    useCallback serves the same purpose as useMemo but it returns a memoized callback instead of a memoized value. In other words, useCallback is the same as passing useMemo without a function call.

    For instance, consider the following codes below.

    import React, {useCallback, useMemo} from 'react'
    
    const MemoizationExample = () => {
      const a = 5
      const b = 7
      
      const memoResult = useMemo(() => a + b, [a, b])
      const callbackResult = useCallback(a + b, [a, b])
    
      console.log(memoResult)
      console.log(callbackResult)
    
      return(
        
    ...
    ) } export default MemoizationExample

    In the above example, both memoResult and callbackResult will give the same value of 12. Here, useCallback will return a memoized value. However, we could also make it return a memoized callback by passing it as a function.

    The useCallback below will return a memoized callback.

    ...
      const callbackResult = useCallback(() => a + b, [a, b])
    ...

    We can then trigger the callback when an action is performed or in a useEffect hook.

    import {useCallback, useEffect} from 'react'
    const memoizationExample = () => {
      const a = 5
      const b = 7
      const callbackResult = useCallback(() => a + b, [a, b])
      useEffect(() => {
        const callback = callbackResult()
        console.log(callback)   
      })
    
      return (
        
    ) } export default memoizationExample

    In the above code, we defined a callback function using the useCallback hook. We then called the callback in a useEffect hook when the component mounts and also when a button is clicked.

    Both the useEffect and the button click yield the same result.

    Note that the concepts, do’s, and don’ts that apply to the useMemo hook also apply to the useCallback hook. We can recreate the useMemo example with useCallback.

    The useRef Hook

    useRef returns an object that can persist in an application. The hook has only one property, currentand we can easily pass an argument to it.

    It serves the same purpose a createRef used in class-based components. We can create a reference with this hook as follows:

    const newRef = useRef('')

    Here we created a new ref called newRef and passed an empty string to it.

    This hook is used mainly for two purposes:

    1. Accessing or manipulating the DOM, and
    2. Storing mutable states — this is useful when we don’t want the component to rerender when a value change.

    Manipulating the DOM

    When passed to a DOM element, the ref object points to that element and can be used to access its DOM attributes and properties.

    Here is a very simple example to demonstrate this concept.

    import React, {useRef, useEffect} from 'react'
    
    const RefExample = () => {
      const headingRef = useRef('')
      console.log(headingRef)
      return(
        

    This is a h1 element

    ) } export default RefExample

    In the example above, we defined headingRef using the useRef hook passing an empty string. We then set the ref in the h1 tag by passing ref = {headingRef}. By setting this ref, we have asked the headingRef to point to our h1 element. This means that we can access the properties of our h1 element from the ref.

    To see this, if we check the value of console.log(headingRef)we’ll get {current: HTMLHeadingElement} or {current: h1} and we can assess all the properties or attributes of the element. A similar thing applies to any other HTML element.

    For instance, we could make the text italic when the component mounts.

    useEffect(() => {
      headingRef.current.style.fontStyle = "italic";
    }, []);

    We can even change the text to something else.

    ...
        headingRef.current.innerHTML = "A Changed H1 Element";
    ...

    We can even change the background color of the parent container as well.

    ...
        headingRef.current.parentNode.style.backgroundColor = "red";
    ...

    Any kind of DOM manipulation can be done here. Observe that headingRef.current can be read in the same way as document.querySelector('.topheading').

    One interesting use case of the useRef hook in manipulating the DOM element is to focus the cursor on the input element. Let’s quickly run through it.

    import {useRef, useEffect} from 'react'
    
    const inputRefExample = () => {
      const inputRef = useRef(null)
      useEffect(() => {
        inputRef.current.focus()
      }, [])
      
      return(
        
    ) } export default inputRefExample

    In the above code, we created inputRef using the useRef hook and then asked it to point to the input element. We then made the cursor focus on the input ref when the component loads and when the button is clicked using inputRef.current.focus(). This is possible because focus() is an attribute of input elements and so the ref will be able to assess the methods.

    Refs created in a parent component can be assessed at the child component by forwarding it using React.forwardRef(). Let’s take a look at it.

    Let’s first create another component NewInput.js and add the following codes to it.

    import { useRef, forwardRef } from "react";
    const NewInput = forwardRef((props, ref) => {
      return ;
    });
    export default NewInput;

    This component accepts props and ref. We passed the ref to its ref prop and props.val to its placeholder prop. Regular React components do not take a ref attribute. This attribute is available only when we wrap it with React.forwardRef as shown above.

    We can then easily call this in the parent component.

    ...
    
    ...

    Storing The Mutable States

    Refs are not just used to manipulate DOM elements, they can also be used to store mutable values without re-rendering the entire component.

    The following example will detect the number of times a button is clicked without re-rendering the component.

    import { useRef } from "react";
    
    export default function App() {
      const countRef = useRef(0);
      const increment = () => {
        countRef.current++;
        console.log(countRef);
      };
      return (
        
    ); }

    In the code above, we incremented the countRef when the button is clicked and then logged it to the console. Although the value is incremented as shown in the console, we won’t be able to see any change if we try to assess it directly in our component. It will only update in the component when it re-renders.

    Note that while useState is asynchronous, useRef is synchronous. In other words, the value is available immediately after it is updated.

    The useLayoutEffect Hook

    Like the useEffect hook, useLayoutEffect is called after the component is mounted and rendered. This hook fires after DOM mutation and it does so synchronously. Apart from getting called synchronously after DOM mutation, useLayoutEffect does the same thing as useEffect.

    useLayoutEffect should only be used for performing DOM mutation or DOM-related measurement, otherwise, you should use the useEffect hook. Using the useEffect hook for DOM mutation functions may cause some performance issues such as flickering but useLayoutEffect handles them perfectly as it runs after the mutations have occurred.

    Let’s take a look at some examples to demonstrate this concept.

    1. We’ll be getting the width and height of the window on resize.
    import {useState, useLayoutEffect} from 'react'
    
    const ResizeExample = () =>{
      const [windowSize, setWindowSize] = useState({width: 0, height: 0})
      useLayoutEffect(() => {
        const resizeWindow = () => setWindowSize({
          width: window.innerWidth,
          height: window.innerHeight
        })
        window.addEventListener('resize', resizeWindow)
        return () => window.removeEventListener('resize', resizeWindow)
      }, [])
    
      return (
        

    width: {windowSize.width}

    height: {windowSize.height}

    ) } export default ResizeExample

    In the above code, we created a state windowSize with width and height properties. Then we set the state to the current window’s width and height respectively when the window is resized. We also cleaned up the code when it unmounts. The clean-up process is essential in useLayoutEffect to clean up the DOM manipulation and improve efficiency.

    1. Let’s blur a text with useLayoutEffect.
    import { useRef, useState, useLayoutEffect } from "react";
    
    export default function App() {
      const paragraphRef = useRef("");
    
      useLayoutEffect(() => {
        const { current } = paragraphRef;
        const blurredEffect = () => {
          current.style.color = "transparent";
          current.style.textShadow = "0 0 5px rgba(0,0,0,0.5)";
        };
        current.addEventListener("click", blurredEffect);
        return () => current.removeEventListener("click", blurredEffect);
      }, []);
    
      return (
        

    This is the text to blur

    ); }

    We used useRef and useLayoutEffect together in the above code. We first created a ref, paragraphRef to point to our paragraph. Then we created an on-click event listener to monitor when the paragraph is clicked and then blurred it using the style properties we defined. Finally, we cleaned up the event listener using removeEventListener.

    The useDispatch And useSelector Hooks

    useDispatch is a Redux hook for dispatching (triggering) actions in an application. It takes an action object as an argument and invokes the action. useDispatch is the hook’s equivalence to mapDispatchToProps.

    On the other hand, useSelector is a Redux hook for assessing Redux states. It takes a function to select the exact Redux reducer from the store and then returns the corresponding states.

    Once our Redux store is connected to a React application through the Redux provider, we can invoke the actions with useDispatch and access the states with useSelector. Every Redux action and state can be assessed with these two hooks.

    Note that these states ship with React Redux (a package that makes assessing the Redux store easy in a React application). They are not available in the core Redux library.

    These hooks are very simple to use. First, we have to declare the dispatch function and then trigger it.

    import {useDispatch, useSelector} from 'react-redux'
    import {useEffect} from 'react'
    const myaction from '...'
    
    const ReduxHooksExample = () =>{
      const dispatch = useDispatch()
      useEffect(() => {
        dispatch(myaction());
        //alternatively, we can do this
        dispatch({type: 'MY_ACTION_TYPE'})
      }, [])       
      
      const mystate = useSelector(state => state.myReducerstate)
      
      return(
        ...
      )
    }
    export default ReduxHooksExample

    In the above code, we imported useDispatch and useSelector from react-redux. Then, in a useEffect hook, we dispatched the action. We could define the action in another file and then call it here or we could define it directly as shown in the useEffect call.

    Once we have dispatched the actions, our states will be available. We can then retrieve the state using the useSelector hook as shown. The states can be used in the same way we would use states from the useState hook.

    Let’s take a look at an example to demonstrate these two hooks.

    To demonstrate this concept, we have to create a Redux store, reducer, and actions. To simplify things here, we’ll be using the Redux Toolkit library with our fake database from JSONPlaceholder.

    We need to install the following packages to get started. Run the following bash commands.

    npm i redux @reduxjs/toolkit react-redux axios

    First, let’s create the employeesSlice.js to handle the reducer and action for our employees’ API.

    import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
    import axios from "axios";
    const endPoint = "https://my-json-server.typicode.com/ifeanyidike/jsondata/employees";
    
    export const fetchEmployees = createAsyncThunk("employees/fetchAll", async () => {
        const { data } = await axios.get(endPoint);
        return data;
    });
    
    const employeesSlice = createSlice({
      name: "employees",
      initialState: { employees: []loading: false, error: "" },
      reducers: {},
      extraReducers: {
        [fetchEmployees.pending]: (state, action) => {
          state.status = "loading";
        },
        [fetchEmployees.fulfilled]: (state, action) => {
          state.status = "success";
          state.employees = action.payload;
        },
        [fetchEmployees.rejected]: (state, action) => {
          state.status = "error";
          state.error = action.error.message;
        }
      }
    });
    export default employeesSlice.reducer;

    This is the standard setup for the Redux toolkit. We used the createAsyncThunk to access the Thunk middleware to perform async actions. This allowed us to fetch the list of employees from the API. We then created the employeesSlice and returned, “loading”, “error”, and the employees’ data depending on the action types.

    Redux toolkit also makes setting up the store easy. Here is the store.

    import { configureStore } from "@reduxjs/toolkit";
    import { combineReducers } from "redux";
    import employeesReducer from "./employeesSlice";
    
    const reducer = combineReducers({
      employees: employeesReducer
    });
    
    export default configureStore({ reducer });;

    Here, we used combineReducers to bundle the reducers and the configureStore function provided by Redux toolkit to set up the store.

    Let’s proceed to use this in our application.

    First, we need to connect Redux to our React application. Ideally, this should be done at the root of our application. I like to do it in the index.js file.

    import React, { StrictMode } from "react";
    import ReactDOM from "react-dom";
    import store from "./redux/store";
    import { Provider } from "react-redux";
    import App from "./App";
    const rootElement = document.getElementById("root");
    ReactDOM.render(
      
        
          
        
      ,
      rootElement
    );

    Here, I’ve imported the store I created above and also Provider from react-redux.

    Then, I wrapped the entire application with the Provider function, passing the store to it. This makes the store accessible throughout our application.

    We can then proceed to use the useDispatch and useSelector hooks to fetch the data.

    Let’s do this in our App.js file.

    import { useDispatch, useSelector } from "react-redux";
    import { fetchEmployees } from "./redux/employeesSlice";
    import { useEffect } from "react";
    
    export default function App() {
      const dispatch = useDispatch();
      useEffect(() => {
        dispatch(fetchEmployees());
      }, [dispatch]);
      const employeesState = useSelector((state) => state.employees);
      const { employees, loading, error } = employeesState;
    
      return (
        
    {loading ? ( "Loading..." ) : error ? (
    {error}
    ) : ( <>

    List of Employees

    {employees.map((employee) => (

    {`${employee.firstName} ${employee.lastName}`}

    ))} )}
    ); }

    In the above code, we used the useDispatch hook to invoke the fetchEmployees action created in the employeesSlice.js file. This makes the employees state to be available in our application. Then, we used the useSelector hook to get the states. Thereafter, we displayed the results by mapping through the employees.

    The useHistory Hook

    Navigation is very important in a React application. While you could achieve this in a couple of ways, React Router provides a simple, efficient and popular way to achieve dynamic routing in a React application. Furthermore, React Router provides a couple of hooks for assessing the state of the router and performing navigation on the browser but to use them, you need to first set up your application properly.

    To use any React Router hook, we should first wrap our application with BrowserRouter. We can then nest the routes with Switch and Route.

    But first, we have to install the package by running the following commands.

    npm install react-router-dom

    Then, we need to set up our application as follows. I like to do this in my App.js file.

    import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
    import Employees from "./components/Employees";
    export default function App() {
      return (
        
    ...
    ); }

    We could have as many Routes as possible depending on the number of components we wish to render. Here, we have rendered only the Employees component. The path attribute tells React Router DOM the path of the component and can be assessed with query string or various other methods.

    The order matters here. The root route should be placed below the child route and so forth. To override this order, you need to include the exact keyword on the root route.

    
      
    

    Now that we have set up the router, we can then use the useHistory hook and other React Router hooks in our application.

    To use the useHistory hook, we need to first declare it as follows.

    import {useHistory} from 'history'
    import {useHistory} from 'react-router-dom'
    
    const Employees = () =>{
      const history = useHistory()
      ...
    }

    If we log history to the console, we’ll see several properties associated with it. These include blockcreateHrefgogoBackgoForwardlengthlistenlocationpushreplace. While all these properties are useful, you will most likely use history.push and history.replace more often than other properties.

    Let’s use this property to move from one page to another.

    Assuming we want to fetch data about a particular employee when we click on their names. We can use the useHistory hook to navigate to the new page where the employee’s information will be displayed.

    function moveToPage = (id) =>{
      history.push(`/employees/${id}`)
    }

    We can implement this in our Employee.js file by adding the following.

    import { useEffect } from "react";
    import { Link, useHistory, useLocation } from "react-router-dom";
    
    export default function Employees() {
      const history = useHistory();
    
      function pushToPage = (id) => {
        history.push(`/employees/${id}`)
      }
      ...
      return (
        
    ...

    List of Employees

    {employees.map((employee) => (
    {`${employee.firstName} ${employee.lastName} `}
    ))}
    ); }

    In the pushToPage function, we used history from the useHistory hook to navigate to the employee’s page and pass the employee id alongside.

    The useLocation Hook

    This hook also ships with React Router DOM. It is a very popular hook used to work with the query string parameter. This hook is similar to the window.location in the browser.

    import {useLocation} from 'react'
    
    const LocationExample = () =>{
      const location = useLocation()
      return (
        ...
      )
    }
    export default LocationExample

    The useLocation hook returns the pathnamesearch parameter, hash and state. The most commonly used parameters include the pathname and search but you could equally use hashand state a lot in your application.

    The location pathname property will return the path we set in our Route set up. While search will return the query search parameter if any. For instance, if we pass 'http://mywebsite.com/employee/?id=1' to our query, the pathname would be /employee and the search would be ?id=1.

    We can then retrieve the various search parameters using packages like query-string or by coding them.

    The useParams Hook

    If we set up our Route with a URL parameter in its path attribute, we can assess those parameters as key/value pairs with the useParams hook.

    For instance, let’s assume that we have the following Route.

    
      
    

    The Route will be expecting a dynamic id in place of :id.

    With the useParams hook, we can assess the id passed by the user, if any.

    For instance, assuming the user passes the following in function with history.push,

    function goToPage = () => {
      history.push(`/employee/3`)
    }

    We can use the useParams hook to access this URL parameter as follows.

    import {useParams} from 'react-router-dom'
    
    const ParamsExample = () =>{
      const params = useParams()
      console.log(params)  
    
      return(
        
    ...
    ) } export default ParamsExample

    If we log params to the console, we’ll get the following object {id: "3"}.

    The useRouteMatch Hook

    This hook provides access to the match object. It returns the closest match to a component if no argument is supplied to it.

    The match object returns several parameters including the path (the same as the path specified in Route), the URLparams object, and isExact.

    For instance, we can use useRouteMatch to return components based on the route.

    import { useRouteMatch } from "react-router-dom";
    import Employees from "...";
    import Admin from "..."
    
    const CustomRoute = () => {
      const match = useRouteMatch("/employees/:id");
      return match ? (
         
      ) : (
        
      );
    };
    export default CustomRoute;

    In the above code, we set a route’s path with useRouteMatch and then rendered the or component depending on the route selected by the user.

    For this to work, we still need to add the route to our App.js file.

    ...
      
        
      
    ...

    Building A Custom Hook

    According to the React documentation, building a custom hook allows us to extract a logic into a reusable function. However, you need to make sure that all the rules that apply to React hooks apply to your custom hook. Check the rules of React hook at the top of this tutorial and ensure that your custom hook complies with each of them.

    Custom hooks allow us to write functions once and reuse them whenever they are needed and hence obeying the DRY principle.

    For instance, we could create a custom hook to get the scroll position on our page as follows.

    import { useLayoutEffect, useState } from "react";
    
    export const useScrollPos = () => {
      const [scrollPos, setScrollPos] = useState({
        x: 0,
        y: 0
      });
      useLayoutEffect(() => {
        const getScrollPos = () =>
          setScrollPos({
            x: window.pageXOffset,
            y: window.pageYOffset
          });
        window.addEventListener("scroll", getScrollPos);
        return () => window.removeEventListener("scroll", getScrollPos);
      }, []);
      return scrollPos;
    };
    

    Here, we defined a custom hook to determine the scroll position on a page. To achieve this, we first created a state, scrollPosto store the scroll position. Since this will be modifying the DOM, we need to use useLayoutEffect instead of useEffect. We added a scroll event listener to capture the x and y scroll positions and then cleaned up the event listener. Finally, we returned to the scroll position.

    We can use this custom hook anywhere in our application by calling it and using it just as we would use any other state.

    import {useScrollPos} from './Scroll'
    
    const App = () =>{
      const scrollPos = useScrollPos()
      console.log(scrollPos.x, scrollPos.y)
      return (
        ...
      )
    }
    export default App

    Here, we imported the custom hook useScrollPos we created above. Then we initialized it and then logged the value to our console. If we scroll on the page, the hook will show us the scroll position at every step of the scroll.

    We can create custom hooks to do just about anything we can imagine in our app. As you can see, we simply need to use the inbuilt React hook to perform some functions. We can also use third-party libraries to create custom hooks but if we do so, we will have to install that library to be able to use the hook.

    Conclusion

    In this tutorial, we took a good look at some useful React hooks you will be using in most of your applications. We examined what they present and how to use them in your application. We also looked at several code examples to help you understand these hooks and apply them to your application.

    I encourage you to try these hooks in your own application to understand them more.

    Resources From The React Docs

    Smashing Editorial(ks, vf, yk, il)




Source link

Revenir vers le haut