Catégories
Astuces et Design

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

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 useRef() hook pour garder une trace des variables sans provoquer de re-rendus, et comment appliquer le re-rendu des composants React.

Dans les composants React, 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. Tandis que useState et useReducer Les hooks sont l'API React pour gérer l'état local dans un composant React, ils peuvent également se faire au prix d'être appelés trop souvent pour que le composant soit rendu à nouveau pour chaque appel effectué aux fonctions de mise à jour.

Dans cet article, je vais vous 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 un nouveau rendu du composant. Et donnez une solution sur la façon d'appliquer le nouveau rendu sans affecter les performances d'un composant.

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

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

{props.title}

{toggled &&
{props.body}
}
) } // Consumed as:

Dans le composant ci-dessus, une carte est rendue à l'aide d'un section élément ayant un enfant h3 avec un card__title classe qui contient le titre de la carte, le corps de la carte est rendu dans une balise article avec le corps de card__body. Nous comptons sur le title et body depuis les 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é.

Scénario - événement mouseover
Scénario – événement de survol de la souris (grand aperçu)

Re-rendu d'un composant avec useState

Le rendu initial d'un composant est effectué lorsqu'un composant a ses valeurs d'état intactes 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 nouveau rendu d'un composant est effectué dans un composant lorsque l'un de ses états locaux ou accessoires a été mis à jour, ce qui oblige 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 Card composant, le mousemove le gestionnaire d'événements appelle le 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 le setToggled fonction de mise à jour de l'état. 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 re-rendu répété utilise un variable locale dans le composant pour conserver 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) {
  let toggled = false;
  
  const handleToggleBody  = () => {
    toggled = !toggled;
    console.log(toggled);
  }
  
  return (
{title}
{toggled &&
{body}
}
) }

Cela s'accompagne d'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.

Utiliser une variable à la place de l'état
Utilisation d'une variable à la place d'un état (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.

  • 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 de son état, React extrait l'ancienne valeur des états et des références de son magasin et réinitialise l'état avec l'ancienne valeur en appliquant également une mise à jour aux états et aux références qui avoir une mise à jour.
  • Il exécute ensuite la fonction du composant pour rendre le composant 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 rendu à nouveau.

Voici un exemple qui peut illustrer ceci:

function Card (props) {
  let toggled = false;
  
  const handleToggleBody = () => {
    toggled = true;
    console.log(toggled);
  };

  useEffect(() => {
    console.log(“Component rendered, the value of toggled is:“, toggled);
  }, (props.title));

  return (
    

{props.title}

{toggled &&
{props.body}
}
); } // Renders the application function App () { const (cardDetails, setCardDetails) = useState({ title: “Something”, body: “uniquely done”, }); useEffect(() => { setTimeout(() => { setCardDetails({ title: “We”, body: “have updated something nice”, }); }, 5000); // Force an update after 5s }, ()); return (
); }

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

le Card a un léger comportement; au lieu de changer l'état basculé, il est réglé sur true lorsqu'un curseur de souris est placé sur le titre de la carte. Aussi un useEffect hook est utilisé pour suivre la valeur du toggled variable après re-rendu.

Utilisation d'une variable à la place de l'état - deuxième test
Utilisation d'une variable à la place de l'é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, pendant ce temps, un re-rendu est déclenché par le composant parent qui réinitialise la variable à l'état initial de false tel que défini dans le composant. Intéressant!

À propos useRef() Crochet

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

Ceci est un titre de 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 = "this is a newer title" ou changer le nom de la classe titleDiv.classList = "This is the class" 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 à l'aide de $ signe. Obtenir le même élément en utilisant jQuery est possible grâce à const el = ("div.title"), le contenu du texte peut également être mis à jour via l'API de jQuery: el.text("New text for the title div").

Réfs en réaction à travers le useRef Crochet

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 un pas plus loin dans le useRef crochet pour un composant fonctionnel.

import React, {useRef, useEffect} from "react";

export default function (props) {
  // Initialized a hook to hold the reference to the title div.
  const titleRef = useRef();
  
  useEffect(function () {
    setTimeout(() => {
      titleRef.current.textContent = "Updated Text"
    }, 2000); // Update the content of the element after 2seconds 
  }, ());
  
  return 
{/** The reference to the element happens here **/ }
Original title
}
Utiliser Ref pour stocker l'état
Utilisation de Ref pour stocker l'état (Grand aperçu)

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

Comment les valeurs sont-elles stockées dans useRef

Une variable Ref dans React est un objet mutable, mais la valeur est conservée par React à travers les re-rendus. Un objet ref a une seule propriété nommée current faire en sorte que les références aient une structure similaire à { current: ReactElementReference }.

La décision de l'équipe React de rendre les ref persistants et mutables doit être considérée comme une décision judicieuse. 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 d'un useRef variable, la nouvelle valeur peut être affectée à la .current d'une variable ref. Cela doit être fait avec prudence 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 sûr.

function User() {
  const name = useRef("Aleem");

  useEffect(() => {
    setTimeout(() => {
      name.current = "Isiaka";
      console.log(name);
    }, 5000);
  });

  return 
{name.current}
; }

Stockage des valeurs dans useRef

Une façon unique de mettre en œuvre un useRef hook 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.

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

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

{props.title}

{toggled &&
{props.body}
}
); ) }

Ce code donne le résultat souhaité en interne mais pas visuellement. La valeur de l'état basculé est persistante mais aucun nouveau rendu n'est effectué lorsque la mise à jour est terminé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.

Rerender peu profond et profond

Dans React, il existe deux mécanismes de rendu, peu profond et Profond le 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 effectuée sur une référence, le mécanisme de rendu superficiel est utilisé pour restituer le composant.

function UserAvatar (props) {
  return 
}

function Username (props) {
  return {props.name}
}

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

  console.log("Original Name", user.current.name);
  console.log("Original Avatar URL", user.current.avatarURL);
  
  useEffect(() => {
    setTimeout(() => {
      user.current = {
        name: "Isiaka Aleem",
        avatarURL: "https://icotar.com/avatar/craig.png?s=50", // a new image
      };
    },5000)
  })
  
  // Both children won't be re-rendered due to shallow rendering mechanism
  // implemented for useRef
  return (
); }

Dans l'exemple ci-dessus, les détails de l'utilisateur sont stockés dans une référence 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'image de l'avatar de l'utilisateur.

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

Rerender peu profond
Shallow-rerender (Grand aperçu)

Le re-rendu profond est utilisé lorsqu'une mise à jour est effectuée sur un état à l'aide de useState hook ou une mise à jour des accessoires du composant.

function UserAvatar (props) {
  return 
}

function Username (props) {
  return {props.name}
}

function User () {
  const (user, setUser) = useState({
    name: "Aleem Isiaka",
    avatarURL: "https://icotar.com/avatar/jake.png?bg=e91e63",
  });

  useEffect(() => {
    setTimeout(() => {
      setUser({
        name: "Isiaka Aleem",
        avatarURL: "https://icotar.com/avatar/craig.png?s=50", // a new image
      });
    },5000);
  })
  
  // Both children are re-rendered due to deep rendering mechanism
  // implemented for useState hook
  return (
); }
Rerender profond
Rerender profond (Grand aperçu)

Contrairement au résultat obtenu lorsque useRef est utilisé, les enfants, dans ce cas, obtiennent la dernière valeur et sont re-rendus pour que leurs interfaces utilisateur aient les effets souhaités.

Forcer un rendu en profondeur pour useRef Mise à jour

Pour obtenir un re-rendu en profondeur lorsqu'une mise à jour est apportée aux refs, le mécanisme de re-rendu en profondeur du useState le crochet peut être partiellement mis en œuvre.

function UserAvatar (props) {
  return 
}

function Username (props) {
  return {props.name}
}

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

  const (, setForceUpdate) = useState(Date.now());
  
  useEffect(() => {
    setTimeout(() => {
      user.current = {
        name: "Isiaka Aleem",
        avatarURL: "https://icotar.com/avatar/craig.png?s=50", // a new image
      };
      
      setForceUpdate();
    },5000)
  })
  return (
); }
Rerender profond + peu profond
Rerender Deep + Shallow (Grand aperçu)

Dans l'amélioration ci-dessus de la User composant, un état est introduit mais sa valeur est ignorée car elle n'est pas requise, tandis que la fonction de mise à jour pour appliquer un rendu du composant est nommée setForceUpdate pour maintenir la convention de dénomination pour useState crochet. Le composant se comporte comme prévu et restitue les enfants une fois que la référence a été mise à jour.

Cela peut soulever des questions telles que:

«N'est-ce pas un anti-modèle?»

ou

«Cela ne fait-il 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é de useRef hook pour stocker les états locaux, et toujours appeler useState crochet pour s'assurer que les enfants obtiennent la dernière valeur du useRef valeur de courant variable qui peut être obtenue avec useState.

Oui, c'est en train de faire presque la même chose que le cas initial mais différemment. le setForceUpdate La fonction 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 tout au long du rendu.

Conclusion

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

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 tout au long du 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 des valeurs dans les refs résout le problème de la répétition fréquente, mais a apporté un nouveau défi au composant qui n’était pas mis à jour après le changement de la valeur d’une référence, ce qui peut être résolu en introduisant un setForceUpdate fonction de mise à jour de l'état.

Dans l'ensemble, les points à retenir sont:

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

Références

Éditorial fracassant(ra, yk, il)

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *