Comment utiliser componentWillMount () dans React Hooks?

176

Dans les documents officiels de React, il mentionne -

Si vous êtes familier avec les méthodes de cycle de vie de classe React, vous pouvez considérer useEffect Hook comme composantDidMount, componentDidUpdate et componentWillUnmount combinés.

Ma question est la suivante: comment pouvons-nous utiliser la componentWillMount()méthode du cycle de vie dans un crochet?

Abrar
la source

Réponses:

340

Vous ne pouvez pas utiliser l' une des méthodes du cycle de vie existants ( componentDidMount, componentDidUpdate, componentWillUnmountetc.) dans un crochet. Ils ne peuvent être utilisés que dans les composants de classe. Et avec Hooks, vous ne pouvez utiliser que des composants fonctionnels. La ligne ci-dessous provient du doc ​​React:

Si vous êtes familier avec les méthodes React du cycle de vie de classe, vous pouvez penser à useEffectcrochet componentDidMount, componentDidUpdateet componentWillUnmountcombiné.

suggèrent que vous pouvez imiter ces méthodes de cycle de vie à partir d'un composant de classe dans un composant fonctionnel.

Le code à l'intérieur componentDidMounts'exécute une fois lorsque le composant est monté. useEffecthook équivalent pour ce comportement est

useEffect(() => {
  // Your code here
}, []);

Notez le deuxième paramètre ici (tableau vide). Cela ne fonctionnera qu'une seule fois.

Sans le deuxième paramètre, le useEffecthook sera appelé sur chaque rendu du composant qui peut être dangereux.

useEffect(() => {
  // Your code here
});

componentWillUnmountest utilisé pour le nettoyage (comme la suppression des écouteurs d'événements, l'annulation du minuteur, etc.). Supposons que vous ajoutez un écouteur d'événements componentDidMountet que vous le supprimez componentWillUnmountcomme ci-dessous.

componentDidMount() {
  window.addEventListener('mousemove', () => {})
}

componentWillUnmount() {
  window.removeEventListener('mousemove', () => {})
}

L'équivalent du crochet du code ci-dessus sera le suivant

useEffect(() => {
  window.addEventListener('mousemove', () => {});

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])
Bhaskar Gyan Vardhan
la source
184
Belle explication pour d'autres événements du cycle de vie, mais ce n'est pas la question spécifiquement d'une alternative à componentWillMount ().
Shiraz
3
Dans les exemples PanResponder que j'ai vus, componentWillMount semble être requis, sinon vous obtenez des panHandlers non définis.
Dror Bar
2
Je comprends maintenant vraiment la useEffect()fonction, merci.
Iharob Al Asimi
67
Pourquoi est-ce une réponse acceptée? Vous n'avez pas mentionné un équivalent de crochet pourcomponentWillMount
Mykybo
3
@techexpert La question demandait un équivalent à componentWillMount, non componentWillUnmount. Cette réponse ne répond en effet pas du tout à la question, et ne fait que réitérer ce que l'OP impliquait déjà de savoir.
JoshuaCWebDeveloper
62

useComponentDidMount hook

Dans la plupart des cas, useComponentDidMountc'est l'outil à utiliser. Il ne s'exécutera qu'une seule fois, après le montage du composant (rendu initial).

 const useComponentDidMount = func => useEffect(func, []);

useComponentWillMount

Il est important de noter qu'en classe, les composants componentWillMountsont considérés comme hérités. Si vous avez besoin de code à exécuter une seule fois avant le montage du composant, vous pouvez utiliser le constructeur. En savoir plus ici . Étant donné que le composant fonctionnel n'a pas l'équivalent d'un constructeur, l'utilisation d'un hook pour exécuter le code une seule fois avant le montage du composant peut avoir un sens dans certains cas. Vous pouvez y parvenir avec un crochet personnalisé.

const useComponentWillMount = func => {
  const willMount = useRef(true);

  if (willMount.current) {
    func();
  }

  willMount.current = false;
};

Cependant, il y a un écueil. Ne l'utilisez pas pour définir votre état de manière asynchrone (par exemple suite à une requête du serveur. Comme vous pourriez vous attendre à ce qu'il affecte le rendu initial, ce qu'il ne fera pas). De tels cas doivent être traités avec useComponentDidMount.

Démo

const Component = (props) => {
  useComponentWillMount(() => console.log("Runs only once before component mounts"));
  useComponentDidMount(() => console.log("Runs only once after component mounts"));
  ...

  return (
    <div>{...}</div>
  );
}

Démo complète

Ben Carp
la source
18
C'est la seule réponse qui répond à la question et qui a du sens. Je vous remercie!
chumakoff le
3
Le seul problème avec cela est que vous obtenez un rendu supplémentaire en raison de la mise à jour d'état impliquée. En utilisant une référence à la place, vous obtenez le comportement souhaité sans le rendu supplémentaire: `const useComponentWillMount = func => {const willMount = useRef (true); useEffect (() => {willMount.current = false;}, []); if (willMount.current) {func (); }}; `
remix23
2
Cette implémentation fonctionnelle de componentWillMountbased on useEffectprésente deux problèmes. Le premier est qu'il n'y a pas de cycle de vie de montage dans les composants fonctionnels, les deux hooks s'exécuteront après le rendu du composant, ce qui Runs only once before component mountsest trompeur. Le second est qu'il componentWillMountest appelé sur le rendu du serveur et useEffectne l'est pas. De nombreuses bibliothèques s'appuient encore sur UNSAFE_componentWillMountcar c'est actuellement le seul moyen de déclencher un effet secondaire côté serveur.
Paolo Moretti
2
@PaoloMoretti, merci. Ce hook componentWillMount n'est pas l'équivalent exact du cycle de vie componentWillMount dans un composant de classe. Cependant, la fonction qui lui est transmise s'exécutera immédiatement, uniquement la première fois qu'elle sera appelée. Cela signifie pratiquement qu'il s'exécutera avant d'être rendu, et avant même de renvoyer une valeur pour la première fois. Pouvons-nous être d'accord là-dessus? Je conviens que l'utilisation du nom componentWillMount n'est pas idéale, car ce nom porte une certaine signification de la version du cycle de vie de la classe. Peut-être que je ferais mieux de l'appeler "useRunPreMount".
Ben Carp
1
@PaoloMoretti, je ne comprends pas tout à fait. Je ne travaille pas avec SSR, mais je suis convaincu que sur le composant SSR, WillMount fonctionne deux fois - une fois sur le serveur et une fois sur le client. Je pense que la même chose est vraie pour le rappel qui est passé à useComponentDidMount. useComponentDidMount relais sur useEffect pour arrêter d'appeler le rappel. Jusqu'à ce que le rappel de useEffect soit exécuté, la fonction du composant s'exécutera deux fois - une fois sur le serveur et une fois sur le client. N'est-ce pas le cas?
Ben Carp
53

Selon reactjs.org, componentWillMount ne sera plus pris en charge à l'avenir. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Il n'est pas nécessaire d'utiliser componentWillMount.

Si vous voulez faire quelque chose avant le montage du composant, faites-le simplement dans le constructeur ().

Si vous souhaitez faire des requêtes réseau, ne le faites pas dans componentWillMount. C'est parce que cela entraînera des bogues inattendus.

Les requêtes réseau peuvent être effectuées dans componentDidMount.

J'espère que ça aide.


mis à jour le 08/03/2019

La raison pour laquelle vous demandez componentWillMount est probablement parce que vous souhaitez initialiser l'état avant les rendus.

Faites-le simplement dans useState.

const helloWorld=()=>{
    const [value,setValue]=useState(0) //initialize your state here
    return <p>{value}</p>
}
export default helloWorld;

ou peut-être que vous souhaitez exécuter une fonction dans componentWillMount, par exemple, si votre code d'origine ressemble à ceci:

componentWillMount(){
  console.log('componentWillMount')
}

avec hook, tout ce que vous avez à faire est de supprimer la méthode du cycle de vie:

const hookComponent=()=>{
    console.log('componentWillMount')
    return <p>you have transfered componeWillMount from class component into hook </p>
}

Je veux juste ajouter quelque chose à la première réponse sur useEffect.

useEffect(()=>{})

useEffect s'exécute sur chaque rendu, c'est une combinaison de componentDidUpdate, componentDidMount et ComponentWillUnmount.

 useEffect(()=>{},[])

Si nous ajoutons un tableau vide dans useEffect, il s'exécute juste lorsque le composant est monté. C'est parce que useEffect comparera le tableau que vous lui avez passé. Il n'est donc pas nécessaire que ce soit un tableau vide, il peut s'agir d'un tableau qui ne change pas. Par exemple, il peut s'agir de [1,2,3] ou ['1,2']. useEffect ne fonctionne toujours que lorsque le composant est monté.

Cela dépend de vous si vous voulez qu'il s'exécute une seule fois ou après chaque rendu. Ce n'est pas dangereux si vous avez oublié d'ajouter un tableau tant que vous savez ce que vous faites.

J'ai créé un échantillon pour le crochet. Vérifie s'il te plaît.

https://codesandbox.io/s/kw6xj153wr


mis à jour le 21/08/2019

C'est un blanc depuis que j'ai écrit la réponse ci-dessus. Il y a quelque chose auquel je pense que vous devez prêter attention. Lorsque vous utilisez

useEffect(()=>{},[])

Lorsque react compare les valeurs que vous avez passées au tableau [], il utilise Object.is () pour comparer. Si vous lui passez un objet, tel que

useEffect(()=>{},[{name:'Tom'}])

C'est exactement la même chose que:

useEffect(()=>{})

Il sera à nouveau rendu à chaque fois car lorsque Object.is () compare un objet, il compare sa référence et non la valeur elle-même. Il en va de même pour la raison pour laquelle {} === {} renvoie false car leurs références sont différentes. Si vous souhaitez toujours comparer l'objet lui-même et non la référence, vous pouvez faire quelque chose comme ceci:

useEffect(()=>{},[JSON.stringify({name:'Tom'})])
MING WU
la source
17
et la question était de savoir comment le mettre en œuvre avec des crochets
Shubham Khatri
3
mais vous n'avez pas besoin de l'implémenter avec des hooks car il ne sera pas pris en charge. Pas besoin d'apprendre à faire cela avec des crochets.
MING WU
1
Maintenant que vous avez mentionné que componentDidMount est le cycle de vie correct à utiliser, vous auriez pu ajouter comment l'implémenter dans votre réponse et votre réponse aurait plus de sens que la réponse acceptée
Shubham Khatri
8
Cela devrait sûrement être la réponse acceptée - cela explique que ComponentWillMount n'est pas disponible dans le paradigme des hooks. L'initialisation des composants fonctionnels est simplifiée - il suffit de faire partie de la fonction
Shiraz
1
En quoi est-ce la même chose que componentWillMount? Si vous lancez du code dans le composant fonctionnel, il exécutera chaque rendu, pas seulement lorsque le composant est sur le point de monter.
Overcode le
13

useLayoutEffectpourrait accomplir cela avec un ensemble vide d'observateurs ( []) si la fonctionnalité est en fait similaire à componentWillMount- elle s'exécutera avant que le premier contenu n'atteigne le DOM - bien qu'il y ait en fait deux mises à jour mais elles sont synchrones avant de dessiner à l'écran.

par exemple:


function MyComponent({ ...andItsProps }) {
     useLayoutEffect(()=> {
          console.log('I am about to render!');
     },[]);

     return (<div>some content</div>);
}

L'avantage par rapport useStateà un initialiseur / setter ou useEffectbien qu'il puisse calculer une passe de rendu, il n'y a pas de re-rendus réels vers le DOM qu'un utilisateur remarquera, et il est exécuté avant le premier rendu notable, ce qui n'est pas le cas pour useEffect. L'inconvénient est bien sûr un léger retard dans votre premier rendu car une vérification / mise à jour doit avoir lieu avant de peindre à l'écran. Cela dépend vraiment de votre cas d'utilisation, cependant.

Personnellement, je pense que useMemoc'est bien dans certains cas de niche où vous devez faire quelque chose de lourd - tant que vous gardez à l'esprit que c'est l'exception par rapport à la norme.

rob2d
la source
3
useLayoutEffect est la voie à suivre !!!! Cette réponse à ma question concernant la vérification si l'utilisateur est connecté. (Le problème était que les composants se chargeraient, puis vérifier si l'utilisateur est connecté.) Ma question est cependant, est-ce que cette pratique standard? Je ne vois pas trop d'endroits
Jessica
1
ouais c'est assez courant; également mentionné dans les documents officiels de React - juste dans un texte plus petit en raison des ramifications du double rendu DOM afin d'exécuter la logique avant qu'un utilisateur ne le remarque.
rob2d
En fait, il s'exécute après le rendu du composant. C'est donc totalement différent de componentWillMount.
Jiri Mihal
7

Voici comment je simule le constructeur dans les composants fonctionnels à l'aide du useRefhook:

function Component(props) {
    const willMount = useRef(true);
    if (willMount.current) {
        console.log('This runs only once before rendering the component.');
        willMount.current = false;        
    }

    return (<h1>Meow world!</h1>);
}

Voici l'exemple de cycle de vie:

function RenderLog(props) {
    console.log('Render log: ' + props.children);
    return (<>{props.children}</>);
}

function Component(props) {

    console.log('Body');
    const [count, setCount] = useState(0);
    const willMount = useRef(true);

    if (willMount.current) {
        console.log('First time load (it runs only once)');
        setCount(2);
        willMount.current = false;
    } else {
        console.log('Repeated load');
    }

    useEffect(() => {
        console.log('Component did mount (it runs only once)');
        return () => console.log('Component will unmount');
    }, []);

    useEffect(() => {
        console.log('Component did update');
    });

    useEffect(() => {
        console.log('Component will receive props');
    }, [count]);


    return (
        <>
        <h1>{count}</h1>
        <RenderLog>{count}</RenderLog>
        </>
    );
}
[Log] Body
[Log] First time load (it runs only once)
[Log] Body
[Log] Repeated load
[Log] Render log: 2
[Log] Component did mount (it runs only once)
[Log] Component did update
[Log] Component will receive props

Bien sûr, les composants de classe n'ont pas d' Bodyétapes, il n'est pas possible de faire une simulation 1: 1 en raison de différents concepts de fonctions et de classes.

Jiri Mihal
la source
Je n'ai pas plongé dans votre exemple, mais votre premier extrait de code fonctionne pour moi, merci!
SAndriy le
6

J'ai écrit un hook personnalisé qui exécutera une fonction une fois avant le premier rendu.

useBeforeFirstRender.js

import { useState, useEffect } from 'react'

export default (fun) => {
  const [hasRendered, setHasRendered] = useState(false)

  useEffect(() => setHasRendered(true), [hasRendered])

  if (!hasRendered) {
    fun()
  }
}

Usage:

import React, { useEffect } from 'react'
import useBeforeFirstRender from '../hooks/useBeforeFirstRender'


export default () => { 
  useBeforeFirstRender(() => {
    console.log('Do stuff here')
  })

  return (
    <div>
      My component
    </div>
  )
}
Peu de temps
la source
3

Il existe une belle solution de contournement à mettre en œuvre componentDidMountet componentWillUnmountavec useEffect.

Basé sur la documentation, useEffectpeut renvoyer une fonction de "nettoyage". cette fonction ne sera pas appelée lors du premier useEffectappel, uniquement lors des appels suivants.

Par conséquent, si nous utilisons le useEffecthook sans aucune dépendance, le hook ne sera appelé que lorsque le composant est monté et la fonction "cleanup" est appelée lorsque le composant est démonté.

useEffect(() => {
    console.log('componentDidMount');

    return () => {
        console.log('componentWillUnmount');
    };
}, []);

L'appel de la fonction de retour de nettoyage est appelé uniquement lorsque le composant est démonté.

J'espère que cela t'aides.

AfikDeri
la source
2
Comment cela aide-t-il si cela n'a rien à voir avec componentWillMount ? Est-ce que je manque quelque chose?
ZenVentzi
Oui, vous manquez le fait que dans le même useEffectappel, vous obtenez la même fonctionnalité componentWillMountet componentWillUnmountd'une manière agréable et propre
AfikDeri
Ce n'est pas vrai, useEffectne s'exécute qu'après un rendu tandis que componentWillMounts'exécute avant le rendu du composant.
Overcode le
@Overcode dont je parlais componentDidMountpas componentWillMount. J'ai raté ça dans la question, mon mauvais.
AfikDeri
1

Vous pouvez pirater le hook useMemo pour imiter un événement de cycle de vie componentWillMount. Faites simplement:

const Component = () => {
   useMemo(() => {
     // componentWillMount events
   },[]);
   useEffect(() => {
     // componentWillMount events
     return () => {
       // componentWillUnmount events
     }
   }, []);
};

Vous devez conserver le hook useMemo avant tout ce qui interagit avec votre état. Ce n'est pas ainsi que cela est prévu, mais cela a fonctionné pour moi pour tous les problèmes de componentWillMount.

Cela fonctionne parce que useMemo ne nécessite pas de retourner une valeur et que vous n'avez pas à l'utiliser comme quoi que ce soit, mais comme il mémorise une valeur basée sur des dépendances qui ne s'exécutera qu'une seule fois ("[]") et c'est au-dessus de notre composant, il s'exécute une fois lorsque le composant se monte avant toute autre chose.

jpmarks
la source
0

https://reactjs.org/docs/hooks-reference.html#usememo

N'oubliez pas que la fonction transmise à useMemo s'exécute pendant le rendu. Ne faites rien là-bas que vous ne feriez normalement pas pendant le rendu. Par exemple, les effets secondaires appartiennent à useEffect et non à useMemo.


la source
usememo est pour l'optimisation des performances. Un crochet sera rendu à nouveau après avoir été déjà monté si un accessoire change, ce qui va à l'encontre du but de l'auteur.
max54
0

La réponse de Ben Carp me semble la seule valable.

Mais comme nous utilisons des moyens fonctionnels, une autre approche peut bénéficier de la fermeture et du HoC:

const InjectWillmount = function(Node, willMountCallback) {
  let isCalled = true;
  return function() {
    if (isCalled) {
      willMountCallback();
      isCalled = false;
    }
    return Node;
  };
};

Alors utilisez-le:

const YourNewComponent = InjectWillmount(<YourComponent />, () => {
  console.log("your pre-mount logic here");
});
Kerem atam
la source
0

Réponse courte à votre question initiale , comment componentWillMountutiliser avec React Hooks:

componentWillMountest obsolète et considérée comme héritée . Recommandation de réaction :

En général, nous vous recommandons d'utiliser le constructeur () à la place pour l'initialisation de l'état.

Maintenant, dans la FAQ Hook, vous découvrez ce qu'est l'équivalent d'un constructeur de classe pour les composants de fonction:

constructeur: les composants de fonction n'ont pas besoin d'un constructeur. Vous pouvez initialiser l'état dans l'appel useState. Si le calcul de l'état initial est coûteux, vous pouvez passer une fonction à useState.

Donc, un exemple d'utilisation de componentWillMountressemble à ceci:

const MyComp = () => {
  const [state, setState] = useState(42) // set initial value directly in useState 
  const [state2, setState2] = useState(createInitVal) // call complex computation

  return <div>{state},{state2}</div>
};

const createInitVal = () => { /* ... complex computation or other logic */ return 42; };
ford04
la source
0

Ajoutez simplement un tableau de dépendances vide dans useEffect, il fonctionnera comme componentDidMount.

useEffect(() => {
  // Your code here
  console.log("componentDidMount")
}, []);
Codage
la source
0

Il existe une astuce simple pour simuler componentDidMountet componentWillUnmounten utilisant useEffect:

useEffect(() => {
  console.log("componentDidMount");

  return () => {
    console.log("componentWillUnmount");
  };
}, []);
AmerllicA
la source