web-dev-qa-db-fra.com

Pourquoi la fonction de nettoyage de `useEffect` est-elle appelée à chaque rendu?

J'ai appris React et j'ai lu que la fonction renvoyée par useEffect est destinée à faire le nettoyage et React effectue le nettoyage lorsque le le composant se démonte.

J'ai donc expérimenté un peu mais j'ai trouvé dans l'exemple suivant que la fonction était appelée à chaque fois que le composant est restitué par opposition au seul moment où il a été démonté du DOM, c'est-à-dire console.log("unmount"); chaque fois que le les composants sont rendus de nouveau.

Pourquoi donc?

function Something({ setShow }) {
  const [array, setArray] = useState([]);
  const myRef = useRef(null);

  useEffect(() => {
    const id = setInterval(() => {
      setArray(array.concat("hello"));
    }, 3000);
    myRef.current = id;
    return () => {
      console.log("unmount");
      clearInterval(myRef.current);
    };
  }, [array]);

  const unmount = () => {
    setShow(false);
  };

  return (
    <div>
      {array.map((item, index) => {
        return (
          <p key={index}>
            {Array(index + 1)
              .fill(item)
              .join("")}
          </p>
        );
      })}
      <button onClick={() => unmount()}>close</button>
    </div>
  );
}

function App() {
  const [show, setShow] = useState(true);

  return show ? <Something setShow={setShow} /> : null;
}

Exemple en direct: https://codesandbox.io/s/vigilant-leavitt-z1jd2

6
Joji

En regardant le code, je pourrais deviner son en raison du deuxième paramètre [array]. Vous le mettez à jour, il appellera donc un nouveau rendu. Essayez de définir un tableau vide.

Chaque mise à jour d'état appellera un nouveau rendu et un démontage, et ce tableau change.

0
Ion

Les documents React ont un section d'explication exactement à ce sujet.

En bref, la raison en est qu'une telle conception protège contre les données périmées et les bogues de mise à jour.

Le crochet useEffect dans React est conçu pour gérer à la fois le rendu initial et tous les rendus suivants ( en savoir plus à ce sujet ).


Les effets sont contrôlés via leurs dépendances et non par le cycle de vie du composant qui les utilise.

À chaque fois que les dépendances d'un effet changent, useEffect nettoiera l'effet précédent et exécutera le nouvel effet.

Une telle conception est plus prévisible - chaque rendu a son propre effet comportemental indépendant (pur) . Cela garantit que l'interface utilisateur affiche toujours les données correctes (car l'interface utilisateur dans le modèle mental de React est une capture d'écran de l'état pour un rendu particulier).

La façon dont nous contrôlons les effets passe par leurs dépendances.

Pour empêcher le nettoyage de s'exécuter sur chaque rendu, il suffit de ne pas modifier les dépendances de l'effet.

Dans votre cas concrètement, le nettoyage a lieu car array est en train de changer, c'est-à-dire Object.is(oldArray, newArray) === false

useEffect(() => {
  // ...
}, [array]);
//  ^^^^^ you're changing the dependency of the effect

Vous provoquez ce changement avec la ligne suivante:

useEffect(() => {
  const id = setInterval(() => {
    setArray(array.concat("hello")); // <-- changing the array changes the effect dep
  }, 3000);
  myRef.current = id;

  return () => {
    clearInterval(myRef.current);
  };
}, [array]); // <-- the array is the effect dep
0
nem035

Cela semble attendu. Selon la documentation ici, useAffects est appelé après le premier rendu, chaque mise à jour et démontage.

https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

Pointe

Si vous connaissez les méthodes de cycle de vie de la classe React, vous pouvez penser à useEffect Hook comme componentDidMount, componentDidUpdate et avant componentWillUnmount combinés.

0
Mobeen