Come usare componentWillMount () in React Hooks?


176

Nei documenti ufficiali di React menziona:

Se hai familiarità con i metodi del ciclo di vita della classe React, puoi pensare a useEffect Hook come componentDidMount, componentDidUpdate e componentWillUnmount combinati.

La mia domanda è: come possiamo usare il componentWillMount()metodo del lifecyle in un gancio?

Risposte:


340

Non è possibile utilizzare uno dei metodi del ciclo di vita esistenti ( componentDidMount, componentDidUpdate, componentWillUnmountecc) in un gancio. Possono essere utilizzati solo nei componenti di classe. E con Hooks è possibile utilizzare solo componenti funzionali. La riga seguente proviene dal documento React:

Se si ha familiarità con i metodi di classe Reagire del ciclo di vita, si può pensare di useEffectHook come componentDidMount, componentDidUpdatee componentWillUnmountcombinato.

suggerire è che è possibile imitare questi metodi del ciclo di vita dal componente di classe in un componente funzionale.

Il codice interno viene componentDidMounteseguito una volta quando il componente è montato. useEffecthook equivalente per questo comportamento è

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

Notare qui il secondo parametro (matrice vuota). Questo verrà eseguito solo una volta.

Senza il secondo parametro l' useEffecthook verrà chiamato su ogni rendering del componente che può essere pericoloso.

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

componentWillUnmountè utilizzato per la pulizia (come rimuovere listener di eventi, annullare il timer ecc.). Supponi di aggiungere un listener di eventi componentDidMounte di rimuoverlo componentWillUnmountcome di seguito.

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

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

L'equivalente hook del codice precedente sarà il seguente

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

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])

184
Bella spiegazione per altri eventi del ciclo di vita, ma questa non è la domanda specifica su un'alternativa a componentWillMount ().
Shiraz,

3
Negli esempi PanResponder ho visto componentWillMount sembra essere richiesto, altrimenti ottieni panHandlers non definiti.
Dror Bar,

2
Ora capisco davvero la useEffect()funzione, grazie.
Iharob Al Asimi,

67
Perché questa è una risposta accettata ?? Non hai menzionato un equivalente hook percomponentWillMount
Mykybo il

3
@techexpert La domanda ha chiesto un equivalente componentWillMount, no componentWillUnmount. Questa risposta in effetti non risponde affatto alla domanda e ribadisce semplicemente ciò che l'OP intendeva già sapere.
JoshuaCWebDeveloper

62

hook useComponentDidMount

Nella maggior parte dei casi useComponentDidMountè lo strumento da utilizzare. Funzionerà solo una volta, dopo che il componente è stato montato (rendering iniziale).

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

useComponentWillMount

È importante notare che i componenti di classe componentWillMountsono considerati legacy. Se è necessario eseguire il codice una sola volta prima del montaggio del componente, è possibile utilizzare il costruttore. Maggiori informazioni qui . Poiché il componente funzionale non ha l'equivalente di un costruttore, l'uso di un hook per eseguire il codice solo una volta prima che i montaggi dei componenti possano avere senso in alcuni casi. Puoi raggiungerlo con un gancio personalizzato.

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

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

  willMount.current = false;
};

Tuttavia, c'è una trappola. Non utilizzarlo per impostare in modo asincrono il tuo stato (ad es. A seguito di una richiesta del server. Poiché potresti aspettarti che influisca sul rendering iniziale che non lo farà). Tali casi dovrebbero essere gestiti con useComponentDidMount.

dimostrazione

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

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

Demo completa


18
Questa è l'unica risposta che risponde alla domanda e ha senso. Grazie!
Chumakoff,

3
L'unico problema è che ottieni un rendering extra a causa dell'aggiornamento dello stato in questione. Usando invece un ref si ottiene il comportamento desiderato senza il rendering extra: `const useComponentWillMount = func => {const willMount = useRef (true); useEffect (() => {willMount.current = false;}, []); if (willMount.current) {func (); }}; `
remix23,

2
Questa implementazione funzionale di componentWillMountbased on useEffectha due problemi. Il primo è che non esiste un ciclo di vita di montaggio nei componenti funzionali, entrambi i ganci verranno eseguiti dopo il rendering del componente, quindi Runs only once before component mountsè fuorviante. Il secondo è che componentWillMountviene chiamato sul rendering del server e useEffectnon lo è. Molte librerie fanno ancora affidamentoUNSAFE_componentWillMount perché al momento è l'unico modo per attivare un lato server con effetti collaterali.
Paolo Moretti,

2
@PaoloMoretti, grazie. Questo hook componentWillMount non è l'equivalente esatto del ciclo di vita di componentWillMount in un componente di classe. Tuttavia, la funzione che gli viene passata verrà eseguita immediatamente, solo la prima volta che viene chiamata. In pratica, ciò significa che verrà eseguito prima del rendering e prima ancora che restituisca un valore per la prima volta. Possiamo essere d'accordo? Sono d'accordo che l'uso del nome componentWillMount non è l'ideale, poiché questo nome ha un certo significato dalla versione del ciclo di vita della classe. Forse è meglio chiamarlo "useRunPreMount".
Ben Carp,

1
@PaoloMoretti, non capisco bene. Non lavoro con SSR, ma la mia comprensione è che sul componente SSR WillMount viene eseguito due volte: una volta sul server e una volta sul client. Penso che lo stesso sia vero per il callback che viene passato a useComponentDidMount. useComponentDidMount inoltra su useEffect per interrompere la chiamata al callback. Fino all'esecuzione del callback di useEffect, la funzione del componente verrà eseguita due volte: una volta sul server e una volta sul client. Non è così?
Ben Carp,

53

Secondo reagjs.org, componentWillMount non sarà supportato in futuro. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Non è necessario utilizzare componentWillMount.

Se vuoi fare qualcosa prima che il componente sia montato, fallo nel costruttore ().

Se si desidera eseguire richieste di rete, non farlo in componentWillMount. È perché questo porterà a bug inaspettati.

Le richieste di rete possono essere eseguite in componentDidMount.

Spero che sia d'aiuto.


aggiornato l'08 / 03/2019

Il motivo per cui si richiede componentWillMount è probabilmente perché si desidera inizializzare lo stato prima del rendering.

Fallo solo in useState.

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

o forse si desidera eseguire una funzione in componentWillMount, ad esempio, se il codice originale è simile al seguente:

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

con hook, tutto ciò che devi fare è rimuovere il metodo del ciclo di vita:

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

Voglio solo aggiungere qualcosa alla prima risposta su useEffect.

useEffect(()=>{})

useEffect viene eseguito su ogni rendering, è una combinazione di componentDidUpdate, componentDidMount e ComponentWillUnmount.

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

Se aggiungiamo un array vuoto in uso, l'effetto viene eseguito solo quando il componente è montato. È perché useEffect confronterà l'array che gli è stato passato. Quindi non deve essere un array vuoto, può essere un array che non cambia. Ad esempio, può essere [1,2,3] o ['1,2']. useEffect funziona ancora solo quando il componente è montato.

Dipende da te se vuoi che venga eseguito una sola volta o venga eseguito dopo ogni rendering. Non è pericoloso se hai dimenticato di aggiungere un array fintanto che sai cosa stai facendo.

Ho creato un campione per hook. Per favore controlla.

https://codesandbox.io/s/kw6xj153wr


aggiornato il 21/08/2019

È stato un bianco da quando ho scritto la risposta sopra. C'è qualcosa a cui penso che tu debba prestare attenzione. Quando si usa

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

Quando reagisce confronta i valori passati all'array [], utilizza Object.is () per confrontare. Se gli passi un oggetto, ad esempio

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

Questo è esattamente lo stesso di:

useEffect(()=>{})

Ri-renderizzerà ogni volta perché quando Object.is () confronta un oggetto, confronta il suo riferimento non il valore stesso. È lo stesso del motivo per cui {} === {} restituisce false perché i loro riferimenti sono diversi. Se vuoi ancora confrontare l'oggetto stesso con il riferimento, puoi fare qualcosa del genere:

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

17
e la domanda era come implementarlo con i ganci
Shubham Khatri il

3
ma non è necessario implementarlo con gli hook perché non sarà supportato. Non c'è bisogno di imparare a farlo con i ganci.
MING MER

1
Ora che hai menzionato che componentDidMount è il ciclo di vita corretto da usare, avresti potuto aggiungere come implementarlo nella tua risposta e quindi la tua risposta avrebbe più senso della risposta accettata
Shubham Khatri,

8
Sicuramente questa dovrebbe essere la risposta accettata - spiega che ComponentWillMount non è disponibile nel paradigma hook. L'inizializzazione dei componenti funzionali è semplificata - deve solo far parte della funzione
Shiraz,

1
Come è la stessa cosa di componentWillMount? Se si lancia il codice nel componente funzionale, verrà eseguito ogni singolo rendering, non solo quando il componente sta per essere montato.
Overcode

13

useLayoutEffectpotrebbe farlo con un set vuoto di osservatori ( []) se la funzionalità è effettivamente simile acomponentWillMount - verrà eseguita prima che il primo contenuto arrivi al DOM - anche se in realtà ci sono due aggiornamenti ma sono sincroni prima di disegnare sullo schermo.

per esempio:


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

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

Il vantaggio rispetto useStatea un inizializzatore / setter o useEffectè se può calcolare un passaggio di rendering, non ci sono veri e propri rendering del DOM che un utente noterà ed è eseguito prima del primo rendering evidente, che non è il caso diuseEffect . Il rovescio della medaglia è ovviamente un leggero ritardo nel tuo primo rendering poiché un controllo / aggiornamento deve avvenire prima di dipingere sullo schermo. Tuttavia, dipende davvero dal tuo caso d'uso.

Penso che personalmente useMemovada bene in alcuni casi di nicchia in cui è necessario fare qualcosa di pesante - purché si tenga presente che è l'eccezione rispetto alla norma.


3
useLayoutEffect è la strada da percorrere !!!! Questa è la risposta alla mia domanda relativa al controllo se l'utente ha effettuato l'accesso. (Il problema era che i componenti venivano caricati, quindi controlla se l'utente ha effettuato l'accesso.) La mia domanda è, è questa pratica standard? Non vedo in troppi posti
Jessica,

1
sì, è abbastanza comune; menzionato anche nei documenti ufficiali di React - solo in un testo più piccolo a causa delle ramificazioni del doppio rendering DOM per eseguire la logica prima che un utente se ne accorga.
rob2d,

In realtà viene eseguito dopo il rendering del componente. Quindi è totalmente diverso da componentWillMount.
Jiri Mihal,

7

Questo è il modo in cui simulo il costruttore in componenti funzionali utilizzando l' 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>);
}

Ecco l'esempio del ciclo di vita:

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

Naturalmente i componenti di classe non hanno Bodypassaggi, non è possibile effettuare una simulazione 1: 1 a causa di diversi concetti di funzioni e classi.


Non mi sono tuffato nel tuo esempio ma il tuo primo frammento di codice funziona per me, grazie!
SAndriy,

6

Ho scritto un hook personalizzato che eseguirà una funzione una volta prima del primo rendering.

useBeforeFirstRender.js

import { useState, useEffect } from 'react'

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

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

  if (!hasRendered) {
    fun()
  }
}

Uso:

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


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

  return (
    <div>
      My component
    </div>
  )
}

3

C'è una bella soluzione da implementare componentDidMounte componentWillUnmountcon useEffect.

Sulla base della documentazione, useEffectpuò restituire una funzione di "pulizia". questa funzione non verrà invocata alla prima useEffectchiamata, ma solo alle chiamate successive.

Pertanto, se utilizziamo il useEffecthook senza dipendenze, il hook verrà chiamato solo quando il componente è montato e la funzione "cleanup" viene chiamata quando il componente è smontato.

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

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

La chiamata della funzione di ritorno cleanup viene invocata solo quando il componente è smontato.

Spero che questo ti aiuti.


2
In che modo aiuta se non ha nulla a che fare con componentWillMount ? Mi sto perdendo qualcosa?
ZenVentzi,

Sì, ti stai perdendo il fatto che nella stessa useEffectchiamata ottieni la stessa funzionalità componentWillMounte componentWillUnmountin un modo bello e pulito
AfikDeri,

Questo non è vero, useEffectviene eseguito solo dopo un rendering mentre componentWillMountviene eseguito prima del rendering del componente.
Overcode

@Overcode di cui parlavo componentDidMountnon componentWillMount. Mi è mancato questo nella domanda, il mio male.
AfikDeri,

1

È possibile hackerare il hook useMemo per imitare un evento del ciclo di vita di ComponentWillMount. Basta fare:

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

Dovresti tenere il gancio useMemo prima di qualsiasi cosa interagisca con il tuo stato. Questo non è come è previsto, ma ha funzionato per me per tutti i problemi di componentWillMount.

Questo funziona perché useMemo non richiede di restituire effettivamente un valore e non è necessario utilizzarlo come qualcosa, ma poiché memorizza un valore basato su dipendenze che verranno eseguite una sola volta ("[]") e si trova sopra il nostro componente esso viene eseguito una volta quando il componente viene montato prima di qualsiasi altra cosa.


0

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

Ricorda che la funzione passata a useMemo viene eseguita durante il rendering. Non fare nulla lì che normalmente non faresti durante il rendering. Ad esempio, gli effetti collaterali appartengono a useEffect, non useMemo.


usememo è per l'ottimizzazione delle prestazioni. Un gancio verrà di nuovo reso dopo essere stato montato in caso di cambio di prop, che vanifica lo scopo dell'autore.
max54,

0

La risposta di Ben Carp mi sembra valida solo per me.

Ma dal momento che stiamo usando modi funzionali, solo un altro approccio può trarre vantaggio dalla chiusura e dall'HoC:

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

Quindi usalo:

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

0

Breve risposta alla tua domanda originale , come componentWillMountpuò essere usato con React Hooks:

componentWillMountè deprecato e considerato legacy . Reagire raccomandazione :

In generale, si consiglia di utilizzare il costruttore () invece per inizializzare lo stato.

Ora nelle FAQ di Hook scopri quale è l'equivalente di un costruttore di classi per i componenti di funzione:

costruttore: i componenti di funzione non hanno bisogno di un costruttore. È possibile inizializzare lo stato nella chiamata useState. Se il calcolo dello stato iniziale è costoso, è possibile passare una funzione per utilizzare State.

Quindi un esempio di utilizzo componentWillMountè simile al seguente:

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; };

0

Basta semplicemente aggiungere un array di dipendenze vuoto in useEffect funzionerà come componentDidMount.

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

0

C'è un semplice trucco per simulare componentDidMounte componentWillUnmountusando useEffect:

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

  return () => {
    console.log("componentWillUnmount");
  };
}, []);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.