Maison > interface Web > js tutoriel > Réagir : fermeture obsolète

Réagir : fermeture obsolète

王林
Libérer: 2024-08-21 06:19:02
original
473 Les gens l'ont consulté

Dans cet article, je vais montrer comment créer une fermeture dans une application React useState hook.

Je n'expliquerai pas ce qu'est une fermeture, car il existe de nombreuses ressources sur ce sujet et je ne veux pas être répétitif. Je conseille la lecture de cet article de @imranabdulmalik.

En bref, une fermeture est (de Mozilla) :

...la combinaison d'une fonction regroupée (ci-jointe) avec des références à son état environnant (l'environnement lexical). En d’autres termes, une fermeture vous donne accès à la portée d’une fonction externe à partir d’une fonction interne. En JavaScript, des fermetures sont créées à chaque fois qu'une fonction est créée, au moment de la création de la fonction.

Juste au cas où vous ne seriez pas familier avec le terme environnement lexical, vous pouvez lire cet article de @soumyadey ou bien celui-ci.

Le problème

Dans une application React, vous pouvez créer accidentellement une fermeture d'une variable appartenant à l'état du composant créé avec le hook useState. Lorsque cela arrive, vous êtes confronté à un problème de fermeture obsolète, c'est-à-dire lorsque vous faites référence à une ancienne valeur de l'État qui, entre-temps, a changé, et donc n'est plus d'actualité.

POC

J'ai créé une application Demo React dont le but principal est d'incrémenter un compteur (appartenant à l'état) qui peut être fermé lors d'une fermeture dans le rappel de la méthode setTimeout.

En bref, cette application peut :

  • Afficher la valeur du compteur
  • Incrémenter de 1 le compteur
  • Démarrez une minuterie pour incrémenter le compteur de 1 après cinq secondes.
  • Incrémenter de 10 le compteur

Dans l'image suivante, l'état initial de l'interface utilisateur de l'application est affiché, avec le compteur à zéro.

React: stale closure

Nous allons simuler la fermeture du compteur en trois étapes :

  1. Incrémentation de 1 le compteur

React: stale closure

  1. Démarrage de la minuterie pour incrémenter de 1 après cinq secondes

React: stale closure

  • Incrémentation de 10 avant le déclenchement du délai d'attente React: stale closure

Après 5 secondes, la valeur du compteur est 2.

React: stale closure

La valeur attendue du compteur devrait être 12, mais nous obtenons 2.

La raison pour laquelle cela se produit est que nous avons créé une fermeture du compteur dans le rappel passé à setTimeout et lorsque le délai d'attente est déclenché, nous réglons le compteur à partir de son ancienne valeur (c'était 1).

setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter + 1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);
Copier après la connexion

En suivant le code complet du composant de l'application.

function App() {
  const [counter, setCounter] = useState(0)
  const timeOutInSeconds: number = 5
  const [startTimeout, setStartTimeout] = useState(false)
  const [timeoutInProgress, setTimeoutInProgress] = useState(false)
  const [logs, setLogs] = useState>([])

  useEffect(() => {
    if (startTimeout && !timeoutInProgress) {
      setTimeoutInProgress(true)
      setLogs((l) => [...l, `Timeout scheduled in ${timeOutInSeconds} seconds`])
      setTimeout(() => {
        setLogs((l) => [...l, `You closed counter with value: ${counter}\n and now I'll increment by one. Check the state`])
        setTimeoutInProgress(false)
        setStartTimeout(false)
        setCounter(counter + 1)
        setLogs((l) => [...l, `Did you create a closure of counter?`])

      }, timeOutInSeconds * 1000);
    }
  }, [counter, startTimeout, timeoutInProgress])

  function renderLogs(): React.ReactNode {
    const listItems = logs.map((log, index) =>
      
  • {log}
  • ); return
      {listItems}
    ; } function updateCounter(value: number) { setCounter(value) setLogs([...logs, `The value of counter is now ${value}`]) } function reset() { setCounter(0) setLogs(["reset done!"]) } return (

    Closure demo


    Counter value: {counter}


    Follow the istructions to create a closure of the state variable counter

    1. Set the counter to preferred value
    2. Start a timeout and wait for {timeOutInSeconds} to increment the counter (current value is {counter})
    3. Increment by 10 the counter before the timeout

    { renderLogs() }
    ); } export default App;
    Copier après la connexion

    Solution

    La solution est basée sur l'utilisation du hook useRef qui permet de référencer une valeur qui n'est pas nécessaire au rendu.

    Nous ajoutons donc au composant App :

    const currentCounter = useRef(counter)
    
    Copier après la connexion

    Ensuite, nous modifierons le rappel de setTimeout comme indiqué ci-dessous :

    setTimeout(() => {
            setLogs((l) => [...l, `You closed counter with value: ${currentCounter.current}\n and now I'll increment by one. Check the state`])
            setTimeoutInProgress(false)
            setStartTimeout(false)
            setCounter(currentCounter.current + 1)
            setLogs((l) => [...l, `Did you create a closure of counter?`])
    
          }, timeOutInSeconds * 1000);
    
    Copier après la connexion

    Notre rappel doit lire la valeur du compteur car nous enregistrons la valeur actuelle avant de l'incrémenter.

    Dans le cas où vous n'avez pas besoin de lire la valeur, vous pouvez éviter la fermeture du compteur en utilisant simplement la notation fonctionnelle pour mettre à jour le compteur.

    seCounter(c => c + 1)
    
    Copier après la connexion

    Ressources

    • Dmitri Pavlutin Soyez conscient des fermetures obsolètes lors de l'utilisation de React Hooks
    • Imran Abdulmalik maîtrisant les fermetures en JavaScript : un guide complet
    • Portée lexicale Keyur Paralkar en JavaScript – Guide du débutant
    • Souvik Paul Fermetures obsolètes dans React
    • Soumya Dey Comprendre la portée lexicale et les fermetures en JavaScript
    • Stackoverflow de Subash Mahapatra

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    source:dev.to
    Déclaration de ce site Web
    Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
    Tutoriels populaires
    Plus>
    Derniers téléchargements
    Plus>
    effets Web
    Code source du site Web
    Matériel du site Web
    Modèle frontal