Fermer

novembre 19, 2020

Une manière réfléchie d'utiliser le crochet useRef () de React


Dans un composant React, useState et useReducer peut provoquer le nouveau rendu de votre composant à chaque fois qu'il y a un appel aux fonctions de mise à jour. Dans cet article, vous découvrirez comment utiliser le hook useRef () pour garder une trace des variables sans provoquer de ré-rendus, et comment appliquer le nouveau rendu des composants React.

In React composants, il y a des moments où des changements fréquents doivent être suivis sans imposer le nouveau rendu du composant. Il se peut également qu'il soit nécessaire de restituer le composant de manière efficace. Alors que les hooks useState et useReducer sont l'API React pour gérer l'état local dans un composant React, ils peuvent également avoir le prix d'être appelés trop souvent, ce qui rend le composant à restituer pour chaque appel effectué aux fonctions de mise à jour.

Dans cet article, je vais expliquer pourquoi useState n'est pas efficace pour suivre certains états, illustrez comment useState crée trop de rendu d'un composant, comment les valeurs stockées dans une variable ne sont pas conservées dans un composant, et enfin, comment useRef peut être utilisé pour garder une trace des variables sans provoquer le rendu du composant. Et donnez une solution sur la façon d'appliquer le re-rendu sans affecter les performances d'un composant.

Après l'évolution des composants fonctionnels, les composants fonctionnels ont la capacité d'avoir un état local qui provoque le re-rendu du composant une fois qu'il y a une mise à jour de n'importe lequel de leur état local.

 function Card (props) {
  const [toggled, setToggled] = useState (faux);
  
  const handleToggleBody = () => {
    setToggled (! toggled)
  }
  
  return (

{props.title}

{basculé &&
{props.body}
}
) } // Consommé comme:

Dans le composant ci-dessus, une carte est rendue en utilisant un élément section ayant un enfant h3 avec une classe card__title qui contient le titre de la carte, le corps de la carte est rendu dans une étiquette d'article avec le corps de card__body . Nous nous appuyons sur le titre et corps des accessoires pour définir le contenu du titre et du corps de la carte, tandis que le corps n'est basculé que lorsque l'en-tête est survolé. [19659008] Scénario – événement mouseover « />

Scénario – événement mouseover ( Grand aperçu )

Rendu d'un composant avec useState

Le rendu initial d'un composant est effectué lorsqu'un composant a son des valeurs d'état parfaites et non diluées, tout comme le composant Card, son rendu initial est lorsque l'événement mouseover n'a pas encore été déclenché. Le rendu d'un composant est effectué dans un composant lorsque l'un de ses états locaux ou accessoires a été mis à jour, ce qui amène le composant à appeler sa méthode de rendu pour afficher les derniers éléments en fonction de la mise à jour de l'état.

Dans le Le composant Card le gestionnaire d'événements mousemove appelle la fonction handleToggleBody pour mettre à jour l'état annulant la valeur précédente de l'état basculé.

Nous pouvons voir ce scénario dans le handleToggleBody fonction appelant la fonction de mise à jour d'état setToggled . Cela provoque l'appel de la fonction chaque fois que l'événement est déclenché.

Stockage des valeurs d'état dans une variable

Une solution de contournement pour le rendu répété consiste à utiliser une variable locale dans le composant pour contenir l'état basculé qui peut également être mis à jour pour empêcher le re-rendu fréquent – qui n'est effectué que lorsqu'il y a une mise à jour des états locaux ou des accessoires d'un composant.

 function Card (props) {
  laissez toggled = false;
  
  const handleToggleBody = () => {
    toggled =! toggled;
    console.log (basculé);
  }
  
  retour (
{title}
{basculé &&
{corps}
}
) }

Cela vient avec un comportement inattendu où la valeur est mise à jour mais le composant n'est pas rendu à nouveau car aucun état interne ou accessoire n'a changé pour déclencher un nouveau rendu du composant.

 Utilisation d'une variable à la place de l'état [19659009] Utilisation d'une variable à la place de l'état (<a href= Grand aperçu )

Les variables locales ne sont pas persistantes dans le rendu

Examinons les étapes du rendu initial à un nouveau rendu d'un composant React. [19659023] Initialement, le composant initialise toutes les variables aux valeurs par défaut, stocke également tous les états et renvoie à un magasin unique tel que défini par l'algorithme React.

  • Lorsqu'une nouvelle mise à jour est disponible pour le composant via une mise à jour de ses accessoires ou state, React extrait l'ancienne valeur des états et des références de son magasin et réinitialise l'état à l'ancienne valeur en appliquant également une mise à jour aux états et aux références qui ont une mise à jour.
  • Il exécute ensuite la fonction du composant pour rendre le composant w avec les états et les références mis à jour. Ce re-rendu réinitialisera également les variables pour conserver leurs valeurs initiales telles que définies dans le composant puisqu'elles ne sont pas suivies.
  • Le composant est ensuite re-rendu.
  • Voici un exemple qui peut illustrer ceci: [19659028] Carte de fonction (accessoires) {
    laissez toggled = false;

    const handleToggleBody = () => {
    toggled = vrai;
    console.log (basculé);
    };

    useEffect (() => {
    console.log ("Composant rendu, la valeur de toggled est:", toggled);
    }, [props.title]);

    revenir (

    {props.title}

    {basculé &&

    {props.body}

    }

    );
    }

    // Rend l'application
    function App () {

    const [cardDetails, setCardDetails] = useState ({
    titre: "Quelque chose",
    corps: "fait uniquement",
    });

    useEffect (() => {
    setTimeout (() => {
    setCardDetails ({
    titre: «Nous»,
    body: "avoir mis à jour quelque chose de gentil",
    });
    }, 5 000); // Forcer une mise à jour après 5s
    }, []);

    revenir (

    );
    }

    Dans le code ci-dessus, le composant Card est rendu en tant qu'enfant dans le composant App . Le composant App repose sur un objet d'état interne nommé cardDetails pour stocker les détails de la carte. De plus, le composant effectue une mise à jour de l'état cardDetails après 5 secondes de rendu initial pour forcer un nouveau rendu de la liste des composants Card .

    The Card a un léger comportement; au lieu de changer l'état basculé, il est mis à true lorsqu'un curseur de souris est placé sur le titre de la carte. De plus, un hook useEffect est utilisé pour suivre la valeur de la variable basculée après le nouveau rendu.

     Utilisation d'une variable à la place de l'état - deuxième test
    Utilisation de la variable dans lieu d'état (deuxième test) ( Grand aperçu )

    Le résultat après avoir exécuté ce code et placé une souris sur le titre met à jour la variable en interne mais ne provoque pas de re-rendu, en attendant, un re -render est déclenché par le composant parent qui réinitialise la variable à l'état initial de false comme défini dans le composant. Intéressant!

    À propos de useRef () Hook

    L'accès aux éléments DOM est le noyau JavaScript du navigateur, en utilisant le JavaScript vanille un élément div avec la classe "title" est accessible en utilisant:

    Ceci est le titre d'un div

    La référence à l'élément peut être utilisée pour faire des choses intéressantes telles que changer le contenu du texte titleDiv.textContent = "ceci est un titre plus récent" ou changer le nom de la classe titleDiv.classList = "C'est la classe" et bien d'autres opérations.

    Au fil du temps, les bibliothèques de manipulation DOM comme jQuery ont rendu ce processus transparent avec un seul appel de fonction utilisant le signe $ . Obtenir le même élément en utilisant jQuery est possible via const el = ("div.title") également le contenu du texte peut être mis à jour via l'API de jQuery: el.text ("Nouveau texte pour le title div ") .

    Refs In React Through The useRef Hook

    ReactJS étant une bibliothèque frontend moderne l'a amené plus loin en fournissant une API Ref pour accéder à son élément, et même une étape plus loin à travers le crochet useRef pour un composant fonctionnel.

     import React, {useRef, useEffect} de "react";
    
    fonction d'exportation par défaut (accessoires) {
      // Initialise un hook pour contenir la référence au div title.
      const titleRef = useRef ();
      
      useEffect (fonction () {
        setTimeout (() => {
          titleRef.current.textContent = "Texte mis à jour"
        }, 2000); // Mettre à jour le contenu de l'élément après 2 secondes
      }, []);
      
      retour 
    {/ ** La référence à l'élément se produit ici ** /}
    Titre original
    }
     Utilisation de Ref pour stocker l'état
    Utilisation de Ref pour stocker l'état ( Grand aperçu )

    Comme vu ci-dessus, après les 2 secondes de l'initiale du composant rendu, le contenu du texte de l'élément div avec le nom de classe du titre devient «Texte mis à jour».

    Comment les valeurs sont stockées dans useRef

    Une variable Ref dans React est une variable mutable objet, mais la valeur est conservée par React à travers les re-rendus. Un objet ref a une seule propriété nommée current rendant les refs ont une structure similaire à {current: ReactElementReference} .

    La décision de l'équipe React de rendre les refs persistants et mutables devrait être considéré comme un sage. Par exemple, lors du re-rendu d'un composant, l'élément DOM peut être mis à jour pendant le processus, alors il est nécessaire que la référence de l'élément DOM soit également mise à jour, et si elle n'est pas mise à jour, la référence doit être conservée. Cela permet d'éviter les incohérences dans le rendu final.

    Mise à jour explicite de la valeur de A useRef Variable

    La mise à jour vers une variable useRef la nouvelle valeur peut être affectée à le .current d'une variable ref. Ceci doit être fait avec précaution lorsqu'une variable ref fait référence à un élément DOM qui peut provoquer un comportement inattendu, à part cela, la mise à jour d'une variable ref est safe .

     function User () {
      nom de const = useRef ("Aleem");
    
      useEffect (() => {
        setTimeout (() => {
          name.current = "Isiaka";
          console.log (nom);
        }, 5 000);
      });
    
      return 
    {nom.courant}
    ; }

    Stockage des valeurs dans useRef

    Une manière unique d'implémenter un hook useRef est de l'utiliser pour stocker des valeurs au lieu de références DOM. Ces valeurs peuvent être soit un état qui n'a pas besoin de changer trop souvent, soit un état qui devrait changer aussi souvent que possible mais ne devrait pas déclencher un nouveau rendu complet du composant.

    Ramenez l'exemple de la carte au lieu de stocker des valeurs comme état, ou variable, une référence est utilisée à la place.

     function Card (props) {
      
      laissez toggled = useRef (false);
      
      const handleToggleBody = () => {
        toggled.current =! toggled.current;
      }
      
      revenir (
        

    {props.title}

    {basculé &&
    {props.body}
    }
    ); ) }

    Ce code donne le résultat souhaité en interne mais pas visuellement. La valeur de l'état basculé est persistante mais aucun rendu n'est effectué lorsque la mise à jour est effectuée, car les références doivent conserver les mêmes valeurs tout au long du cycle de vie d'un composant, React ne s'attend pas à ce qu'elles changent.

    Rendu peu profond et profond

    Dans React, il existe deux mécanismes de rendu, peu profond et profond rendu. Le rendu superficiel affecte uniquement le composant et non les enfants, tandis que le rendu profond affecte le composant lui-même et tous ses enfants.

    Lorsqu'une mise à jour est apportée à une référence, le mécanisme de rendu superficiel est utilisé pour restituer le composant. [19659028] fonction UserAvatar (accessoires) {
    retour
    }

    function Nom d'utilisateur (accessoires) {
    return {props.name}
    }

    function Utilisateur () {
    utilisateur const = useRef ({
    nom: "Aleem Isiaka",
    avatarURL: "https://icotar.com/avatar/jake.png?bg=e91e63",
    })

    console.log ("Nom d'origine", nom.utilisateur.courant);
    console.log ("URL de l'avatar d'origine", user.current.avatarURL);

    useEffect (() => {
    setTimeout (() => {
    user.current = {
    nom: "Isiaka Aleem",
    avatarURL: "https://icotar.com/avatar/craig.png?s=50", // une nouvelle image
    };
    }, 5000)
    })

    // Les deux enfants ne seront pas rendus en raison d'un mécanisme de rendu peu profond
    // implémenté pour useRef
    retour (


    );
    }

    Dans l'exemple ci-dessus, les détails de l'utilisateur sont stockés dans une ref qui est mise à jour après 5 secondes, le composant User a deux enfants, Username pour afficher le nom de l'utilisateur et UserAvatar pour afficher l'avatar de l'utilisateur image.

    Une fois la mise à jour effectuée, la valeur de useRef est mise à jour mais les enfants ne mettent pas à jour leur interface utilisateur car ils ne sont pas rendus à nouveau. Il s'agit d'un re-rendu peu profond, et c'est ce qui est implémenté pour le hook useRef.

     Shallow-rerender
    Shallow-rerender ( Large preview )

    Le re-rendu profond est utilisé lorsqu'un la mise à jour est effectuée sur un état en utilisant le hook useState ou une mise à jour des accessoires du composant.

     function UserAvatar (props) {
      retour 
    }
    
    function Nom d'utilisateur (accessoires) {
      return  {props.name} 
    }
    
    function Utilisateur () {
      const [user, setUser] = useState ({
        nom: "Aleem Isiaka",
        avatarURL: "https://icotar.com/avatar/jake.png?bg=e91e63",
      });
    
      useEffect (() => {
        setTimeout (() => {
          setUser ({
            nom: "Isiaka Aleem",
            avatarURL: "https://icotar.com/avatar/craig.png?s=50", // une nouvelle image
          });
        }, 5 000);
      })
      
      // Les deux enfants sont re-rendus en raison d'un mécanisme de rendu profond
      // implémenté pour le hook useState
      retour (
    ); }
     Deep rerender
    Deep rerender ( Large preview )

    Contrairement au résultat obtenu lorsque useRef est utilisé, les enfants, dans ce cas, obtiennent la dernière

    Forcer un re-rendu profond pour useRef Update

    Pour obtenir un re-rendu profond lorsqu'une mise à jour est faite aux références, le mécanisme de restitution en profondeur du hook useState peut être partiellement implémenté.

     function UserAvatar (props) {
      retour 
    }
    
    function Nom d'utilisateur (accessoires) {
      return  {props.name} 
    }
    
    function Utilisateur () {
      utilisateur const = useRef ({
        nom: "Aleem Isiaka",
        avatarURL: "https://icotar.com/avatar/jake.png?bg=e91e63",
      })
    
      const [, setForceUpdate] = useState (Date.now ());
      
      useEffect (() => {
        setTimeout (() => {
          user.current = {
            nom: "Isiaka Aleem",
            avatarURL: "https://icotar.com/avatar/craig.png?s=50", // une nouvelle image
          };
          
          setForceUpdate ();
        }, 5000)
      })
      retour (
    ); }
     Rerender Deep + Shallow
    Rerender Deep + Shallow ( Large preview )

    Dans l'amélioration ci-dessus du composant User un état est introduit mais sa valeur est ignoré car il n'est pas nécessaire, tandis que la fonction de mise à jour pour appliquer un rendu du composant est nommée setForceUpdate pour conserver la convention de dénomination du hook useState . Le composant se comporte comme prévu et restitue les enfants après la mise à jour de la référence.

    Cela peut soulever des questions telles que:

    "N'est-ce pas un anti-pattern?"

    ou

    "Is cela ne fait pas la même chose que le problème initial mais différemment? »

    Bien sûr, c'est un anti-pattern, car nous profitons de la flexibilité du crochet useRef pour stocker les états locaux, et toujours en appelant le hook useState pour s'assurer que les enfants obtiennent la dernière valeur de la valeur actuelle de la variable useRef qui peut être obtenue avec useState .

    Oui fait presque la même chose que le cas initial mais différemment. La fonction setForceUpdate effectue un re-rendu en profondeur mais ne met à jour aucun état qui agit sur l'élément du composant, ce qui le maintient cohérent à travers le rendu.

    Conclusion

    Mise à jour fréquente de l'état dans un Le composant React utilisant le hook useState peut provoquer des effets indésirables. Nous avons également vu que les variables peuvent être une option incontournable; elles ne sont pas persistantes pendant le rendu d'un composant comme un état est persistant.

    Les références dans React sont utilisées pour stocker une référence à un élément React et leurs valeurs sont conservées à travers le rendu. Les refs sont des objets mutables, ils peuvent donc être mis à jour explicitement et peuvent contenir des valeurs autres qu'une référence à un élément React.

    Le stockage de valeurs dans refs résout le problème de la répétition fréquente mais a apporté un nouveau défi du composant non mis à jour après que la valeur d'une référence a changé, ce qui peut être résolu en introduisant une fonction de mise à jour de l'état setForceUpdate .

    Dans l'ensemble, les points à retenir sont:

    • Nous pouvons stocker les valeurs dans les références et les mettre à jour, ce qui est plus efficace que useState qui peut être coûteux lorsque les valeurs doivent être mises à jour plusieurs fois en une seconde.
    • Nous pouvons forcer React à restituer un composant, même si aucune mise à jour n'est nécessaire en utilisant une fonction de mise à jour non-référence useState .
    • Nous pouvons combiner 1 et 2 pour avoir un composant haute performance en constante évolution.

    Références

     Smashing Editorial (ra , yk, il)




    Source link