web-dev-qa-db-fra.com

Comment enregistrer un événement avec des hooks useEffect?

Je suis un cours Udemy sur la façon d'enregistrer des événements avec des hooks, l'instructeur a donné le code ci-dessous:

  const [userText, setUserText] = useState('');

  const handleUserKeyPress = event => {
    const { key, keyCode } = event;

    if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
      setUserText(`${userText}${key}`);
    }
  };

  useEffect(() => {
    window.addEventListener('keydown', handleUserKeyPress);

    return () => {
      window.removeEventListener('keydown', handleUserKeyPress);
    };
  });

  return (
    <div>
      <h1>Feel free to type!</h1>
      <blockquote>{userText}</blockquote>
    </div>
  );

Maintenant, cela fonctionne très bien, mais je ne suis pas convaincu que ce soit la bonne façon. La raison en est, si je comprends bien, à chaque re-rendu, les événements continueront de s'inscrire et de se désenregistrer à chaque fois et je ne pense tout simplement pas que ce soit la bonne façon de procéder.

J'ai donc apporté une légère modification aux crochets useEffect ci-dessous

useEffect(() => {
  window.addEventListener('keydown', handleUserKeyPress);

  return () => {
    window.removeEventListener('keydown', handleUserKeyPress);
  };
}, []);

En ayant un tableau vide comme deuxième argument, permettant au composant d'exécuter l'effet une seule fois, en imitant componentDidMount. Et quand j'essaie le résultat, c'est bizarre que sur chaque touche que je tape, au lieu de l'ajouter, elle soit remplacée à la place.

Je m'attendais à setUserText (${userText}${key}); pour que la nouvelle clé tapée soit ajoutée à l'état actuel et définie comme un nouvel état, mais à la place, elle oublie l'ancien état et réécrit avec le nouvel état.

Était-ce vraiment la bonne façon d'enregistrer et de désenregistrer l'événement à chaque re-rendu?

39
Isaac

La meilleure façon de gérer de tels scénarios est de voir ce que vous faites dans le gestionnaire d'événements. Si vous définissez simplement l'état en utilisant l'état précédent, il est préférable d'utiliser le modèle de rappel et d'enregistrer les écouteurs d'événements uniquement lors du montage initial. Si vous n'utilisez pas le callback pattern ( https://reactjs.org/docs/hooks-reference.html#usecallback ) la référence des écouteurs avec sa portée lexicale est utilisée par l'écouteur d'événements mais une nouvelle fonction est créée avec fermeture mise à jour sur le nouveau rendu et donc dans le gestionnaire vous ne pourrez pas l'état mis à jour

const [userText, setUserText] = useState('');

  const handleUserKeyPress = useCallback(event => {
    const { key, keyCode } = event;

    if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
      setUserText(prevUserText => `${prevUserText}${key}`);
    }
  }, []);

  useEffect(() => {
    window.addEventListener('keydown', handleUserKeyPress);

    return () => {
      window.removeEventListener('keydown', handleUserKeyPress);
    };
  }, [handleUserKeyPress]);

  return (
    <div>
      <h1>Feel free to type!</h1>
      <blockquote>{userText}</blockquote>
    </div>
  );
46
Shubham Khatri

nouvelle réponse:

useEffect(() => {
  function handlekeydownEvent(event) {
    const { key, keyCode } = event;
    if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
      setUserText(prevUserText => `${prevUserText}${key}`);
    }
  }

  document.addEventListener('keyup', handlekeydownEvent)
  return () => {
    document.removeEventListener('keyup', handlekeydownEvent)
  }
}, [])

lorsque vous utilisez setUserText, passez la fonction comme argument au lieu de l'objet, prevUserText sera toujours le dernier état.


ancienne réponse:

essayez ceci, il fonctionne de la même manière que votre code d'origine:

useEffect(() => {
  function handlekeydownEvent(event) {
    const { key, keyCode } = event;
    if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
      setUserText(`${userText}${key}`);
    }
  }

  document.addEventListener('keyup', handlekeydownEvent)
  return () => {
    document.removeEventListener('keyup', handlekeydownEvent)
  }
}, [userText])

car dans votre méthode useEffect(), cela dépend de la variable userText mais vous ne la placez pas dans le deuxième argument, sinon le userText sera toujours lié à l'initiale valeur '' avec l'argument [].

vous n'avez pas besoin de faire comme ça, je veux juste vous faire savoir pourquoi votre deuxième solution ne fonctionne pas.

3
Spark.Bao

Pour votre cas d'utilisation, useEffect a besoin d'un tableau de dépendances pour suivre les modifications et en fonction de la dépendance, il peut déterminer s'il faut restituer ou non. Il est toujours conseillé de passer un tableau de dépendances à useEffect. Veuillez voir le code ci-dessous:

J'ai introduit useCallback hook.

const { useCallback, useState, useEffect } = React;

  const [userText, setUserText] = useState("");

  const handleUserKeyPress = useCallback(event => {
    const { key, keyCode } = event;

    if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
      setUserText(prevUserText => `${prevUserText}${key}`);
    }
  }, []);

  useEffect(() => {
    window.addEventListener("keydown", handleUserKeyPress);

    return () => {
      window.removeEventListener("keydown", handleUserKeyPress);
    };
  }, [handleUserKeyPress]);

  return (
    <div>
      <blockquote>{userText}</blockquote>
    </div>
  );

Edit q98jov5kvq

1
John Kennedy

Vous aurez besoin d'un moyen de garder une trace de l'état précédent. useState vous aide à garder une trace de l'état actuel uniquement. Depuis le docs , il existe un moyen d'accéder à l'ancien état, en utilisant un autre hook.

const prevRef = useRef();
useEffect(() => {
  prevRef.current = userText;
});

J'ai mis à jour votre exemple pour l'utiliser. Et ça marche.

const { useState, useEffect, useRef } = React;

const App = () => {
  const [userText, setUserText] = useState("");
  const prevRef = useRef();
  useEffect(() => {
    prevRef.current = userText;
  });

  const handleUserKeyPress = event => {
    const { key, keyCode } = event;

    if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
      setUserText(`${prevRef.current}${key}`);
    }
  };

  useEffect(() => {
    window.addEventListener("keydown", handleUserKeyPress);

    return () => {
      window.removeEventListener("keydown", handleUserKeyPress);
    };
  }, []);

  return (
    <div>
      <h1>Feel free to type!</h1>
      <blockquote>{userText}</blockquote>
    </div>
  );
};

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
0
Maaz Syed Adeeb