Comment appeler la fonction de chargement avec React useEffect une seule fois

210

Le hook useEffect React exécutera la fonction passée à chaque modification. Cela peut être optimisé pour qu'il n'appelle que lorsque les propriétés souhaitées changent.

Que faire si je veux appeler une fonction d'initialisation à partir de componentDidMountet ne pas la rappeler en cas de modifications? Disons que je veux charger une entité, mais la fonction de chargement n'a pas besoin de données du composant. Comment pouvons-nous faire cela en utilisant le useEffectcrochet?

class MyComponent extends React.PureComponent {
    componentDidMount() {
        loadDataOnlyOnce();
    }
    render() { ... }
}

Avec des crochets, cela pourrait ressembler à ceci:

function MyComponent() {
    useEffect(() => {
        loadDataOnlyOnce(); // this will fire on every change :(
    }, [...???]);
    return (...);
}
Dávid Molnár
la source

Réponses:

397

Si vous ne voulez exécuter la fonction donnée useEffectqu'après le rendu initial, vous pouvez lui donner un tableau vide comme deuxième argument.

function MyComponent() {
  useEffect(() => {
    loadDataOnlyOnce();
  }, []);

  return <div> {/* ... */} </div>;
}
Tholle
la source
28
Sinon, s'il y a des paramètres que vous utilisez pour récupérer les données (par exemple un identifiant d'utilisateur), vous pouvez passer l'identifiant d'utilisateur dans ce tableau et s'il change, le composant récupérera les données. De nombreux cas d'utilisation fonctionneront comme ça.
trixn
4
yep ... plus d'informations sur les sauts sont documentées ici: reactjs.org/docs/…
Melounek
Cela semble être la réponse la plus simple, mais ESLint se plaint ... voir une autre réponse sur ce fil stackoverflow.com/a/56767883/1550587
Simon Hutchison
Passez simplement loadDataOnlyOnce dans le tableau des dépendances. Ça marche?
jpmarks le
88

TL; DR

useEffect(yourCallback, []) - ne déclenchera le rappel qu'après le premier rendu.

Explication détaillée

useEffects'exécute par défaut après chaque rendu du composant (provoquant ainsi un effet).

Lorsque vous placez useEffectvotre composant, vous indiquez à React que vous souhaitez exécuter le rappel en tant qu'effet. React exécutera l'effet après le rendu et après avoir effectué les mises à jour DOM.

Si vous ne transmettez qu'un rappel, le rappel s'exécutera après chaque rendu.

Si vous passez un deuxième argument (tableau), React exécutera le rappel après le premier rendu et à chaque fois qu'un des éléments du tableau est modifié. par exemple lors du placement useEffect(() => console.log('hello'), [someVar, someOtherVar])- le rappel s'exécutera après le premier rendu et après tout rendu dont l'un someVarou someOtherVarest modifié.

En passant le deuxième argument un tableau vide, React comparera après chaque rendu le tableau et verra que rien n'a été changé, appelant ainsi le rappel uniquement après le premier rendu.

Edan Chetrit
la source
70

crochet useMountEffect

Exécuter une fonction une seule fois après le montage des composants est un modèle si courant qu'il justifie un hook qui lui est propre qui cache les détails de l'implémentation.

const useMountEffect = (fun) => useEffect(fun, [])

Utilisez-le dans n'importe quel composant fonctionnel.

function MyComponent() {
    useMountEffect(function) // function will run only once after it has mounted. 
    return <div>...</div>;
}

À propos du hook useMountEffect

Lors de l'utilisation useEffectavec un deuxième argument de tableau, React exécutera le rappel après le montage (rendu initial) et après que les valeurs du tableau aient changé. Puisque nous passons un tableau vide, il ne fonctionnera qu'après le montage.

Ben Carp
la source
19
Je préfère fortement votre réponse, car la règle ESLint "react-hooks / exhaust-deps" échouera toujours sur des listes de dépendances vides. Et par exemple, le célèbre modèle create-react-app appliquera cette règle.
Dynalon
1
Tout à fait d'accord avec @Dynalon. Cela devrait être la solution acceptée car elle n'interfère pas avec la règle ESLint
Mikado68
Merci @Dynalon et Mikado68 :-). La décision est un privilège du PO. J'ai été informé de vos commentaires, mais le PO ne l'a pas été. Vous pouvez lui suggérer en commentant directement la question.
Ben Carp
2
Vous pouvez maintenant l'utiliser useMountlorsque votre fonction d'effet a besoin de quelque chose des accessoires mais n'a jamais besoin de s'exécuter à nouveau même si cette valeur change sans linter warnig: useEffect(()=>console.log(props.val),[])aura un avertissement de dépendance manquant mais useMount(()=>console.log(props.val))ne provoquera pas d'avertissement mais "fonctionne". Je ne suis pas sûr s'il y aura un problème avec le mode concurrent.
HMR
1
J'aime celui-ci :) Même raison que précédemment; la règle ESLint ne se plaindra pas de celle-ci, et son nom est plus facile à comprendre qu'un tableau vide
Frexuz
20

Passez un tableau vide comme deuxième argument à useEffect. Cela indique efficacement à React, en citant les documents :

Cela indique à React que votre effet ne dépend d'aucune valeur des accessoires ou de l'état, il n'a donc jamais besoin d'être réexécuté.

Voici un extrait que vous pouvez exécuter pour montrer que cela fonctionne:

function App() {
  const [user, setUser] = React.useState(null);

  React.useEffect(() => {
    fetch('https://randomuser.me/api/')
      .then(results => results.json())
      .then(data => {
        setUser(data.results[0]);
      });
  }, []); // Pass empty array to only run once on mount.
  
  return <div>
    {user ? user.name.first : 'Loading...'}
  </div>;
}

ReactDOM.render(<App/>, document.getElementById('app'));
<script src="https://unpkg.com/[email protected]/umd/react.development.js"></script>
<script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script>

<div id="app"></div>

Yangshun Tay
la source
4

J'aime définir une mountfonction, cela trompe EsLint de la même manière useMountet je trouve cela plus explicite.

const mount = () => {
  console.log('mounted')
  // ...

  const unmount = () => {
    console.log('unmounted')
    // ...
  }
  return unmount
}
useEffect(mount, [])
écoologique
la source
-2

L'astuce est que useEffect prend un deuxième paramètre.

Le deuxième paramètre est un tableau de variables que le composant vérifiera pour s'assurer qu'il a changé avant de refaire le rendu. Vous pouvez mettre ici tous les éléments d'accessoires et l'état que vous souhaitez vérifier.

Ou, ne mettez rien:

import React, { useEffect } from 'react';

function App() {
  useEffect(() => {

    // Run! Like go get some data from an API.

  }, []); //Empty array as second argument

  return (
    <div>
      {/* Do something with data. */}
    </div>
  );
}

Cela garantira que useEffect ne s'exécute qu'une seule fois.

Note de la documentation:

Si vous utilisez cette optimisation, assurez-vous que le tableau inclut toutes les valeurs de la portée du composant (telles que les accessoires et l'état) qui changent au fil du temps et qui sont utilisées par l'effet. Sinon, votre code référencera les valeurs obsolètes des rendus précédents.

Farrukh Malik
la source
4
Si vous copiez / collez littéralement à partir de CSS Tricks, le moins que vous puissiez faire est de créditer votre source. css-tricks.com/run-useeffect-only-once
burtyish