Wie verwende ich componentWillMount () in React Hooks?


174

In den offiziellen Dokumenten von React wird erwähnt -

Wenn Sie mit den Lebenszyklusmethoden der React-Klasse vertraut sind, können Sie sich useEffect Hook als die Komponenten componentDidMount, componentDidUpdate und componentWillUnmount vorstellen.

Meine Frage ist - wie können wir die componentWillMount()Lifecyle-Methode in einem Hook verwenden?

Antworten:


331

Sie können keine der bestehenden Lebenszyklus Methoden (Verwendung componentDidMount, componentDidUpdate, componentWillUnmountetc.) in einem Haken. Sie können nur in Klassenkomponenten verwendet werden. Und mit Hooks können Sie nur in Funktionskomponenten verwenden. Die folgende Zeile stammt aus dem React-Dokument:

Wenn Sie mit React Klasse Lifecycle Methoden vertraut sind, können Sie sich vorstellen useEffectHaken wie componentDidMount, componentDidUpdateund componentWillUnmountkombiniert.

Es wird vorgeschlagen, dass Sie diese Lebenszyklusmethode aus einer Klassenkomponente in einer Funktionskomponente nachahmen können.

Code im Inneren componentDidMountwird einmal ausgeführt, wenn die Komponente bereitgestellt wird. useEffectHakenäquivalent für dieses Verhalten ist

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

Beachten Sie hier den zweiten Parameter (leeres Array). Dies wird nur einmal ausgeführt.

Ohne den zweiten Parameter wird der useEffectHook bei jedem Rendering der Komponente aufgerufen, was gefährlich sein kann.

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

componentWillUnmountwird zur Bereinigung verwendet (z. B. Entfernen von Ereignis-Listenern, Abbrechen des Timers usw.). Angenommen, Sie fügen einen Ereignis-Listener hinzu componentDidMountund entfernen ihn componentWillUnmountwie folgt.

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

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

Das Hook-Äquivalent des obigen Codes lautet wie folgt

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

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

183
Gute Erklärung für andere Lifecycle-Ereignisse, aber dies ist nicht die Frage nach einer Alternative zu componentWillMount ().
Shiraz

3
In den PanResponder-Beispielen, die ich gesehen habe, scheint componentWillMount erforderlich zu sein, andernfalls erhalten Sie undefinierte panHandler.
Dror Bar

2
Ich verstehe jetzt die useEffect()Funktion wirklich , danke.
Iharob Al Asimi

67
Warum ist das eine akzeptierte Antwort? Sie haben kein componentWillMount
Hakenäquivalent

3
@techexpert Die Frage nach einem Äquivalent zu componentWillMount, nicht componentWillUnmount. Diese Antwort beantwortet die Frage in der Tat überhaupt nicht und wiederholt nur, was das OP bereits impliziert hat, um zu wissen.
JoshuaCWebDeveloper

60

useComponentDidMount-Hook

In den meisten Fällen useComponentDidMountist das Werkzeug zu verwenden. Es wird nur einmal ausgeführt, nachdem die Komponente bereitgestellt wurde (anfängliches Rendern).

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

useComponentWillMount

Es ist wichtig zu beachten, dass Komponenten in Klassen componentWillMountals Legacy betrachtet werden. Wenn Sie Code nur einmal ausführen müssen, bevor die Komponente bereitgestellt wurde, können Sie den Konstruktor verwenden. Mehr dazu hier . Da die Funktionskomponente nicht die Äquivalenz eines Konstruktors hat, kann es in bestimmten Fällen sinnvoll sein, einen Hook zu verwenden, um Code nur einmal auszuführen, bevor Komponenten bereitgestellt werden. Sie können es mit einem benutzerdefinierten Haken erreichen.

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

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

  willMount.current = false;
};

Es gibt jedoch eine Falle. Verwenden Sie es nicht, um Ihren Status asynchron festzulegen (z. B. nach einer Serveranforderung. Wie zu erwarten ist, wirkt sich dies auf das anfängliche Rendern aus, das nicht erfolgt). Solche Fälle sollten behandelt werden useComponentDidMount.

Demo

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

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

Vollständige Demo


17
Dies ist die einzige Antwort, die die Frage beantwortet und Sinn macht. Danke dir!
Chumakoff

3
Das einzige Problem dabei ist, dass Sie aufgrund der Statusaktualisierung ein zusätzliches Rendering erhalten. Wenn Sie stattdessen eine Referenz verwenden, erhalten Sie das gewünschte Verhalten ohne das zusätzliche Rendern: `const useComponentWillMount = func => {const willMount = useRef (true); useEffect (() => {willMount.current = false;}, []); if (willMount.current) {func (); }}; `
remix23

2
Diese funktionale Implementierung von componentWillMountbasiert auf useEffecthat zwei Probleme. Der erste ist, dass es in funktionalen Komponenten keinen Montagelebenszyklus gibt. Beide Hooks werden ausgeführt, nachdem die Komponente gerendert wurde, Runs only once before component mountswas irreführend ist. Die zweite ist, dass componentWillMountbeim Server-Rendering aufgerufen wird und useEffectnicht. Viele Bibliotheken verlassen sich immer noch darauf, UNSAFE_componentWillMountda dies derzeit die einzige Möglichkeit ist, eine Nebenwirkung auf der Serverseite auszulösen.
Paolo Moretti

2
@PaoloMoretti, danke. Dieser componentWillMount-Hook entspricht nicht genau dem componentWillMount-Lebenszyklus in einer Klassenkomponente. Die an sie übergebene Funktion wird jedoch sofort ausgeführt, nur beim ersten Aufruf. Dies bedeutet praktisch, dass es ausgeführt wird, bevor es gerendert wird und bevor es zum ersten Mal einen Wert zurückgibt. Können wir uns darüber einig sein? Ich bin damit einverstanden, dass die Verwendung des Namens componentWillMount nicht ideal ist, da dieser Name eine bestimmte Bedeutung aus der Klassenlebenszyklusversion hat. Vielleicht nenne ich es besser "useRunPreMount".
Ben Carp

1
@PaoloMoretti, ich verstehe es nicht ganz. Ich arbeite nicht mit SSR, aber ich gehe davon aus, dass auf SSR-Komponenten WillMount zweimal ausgeführt wird - einmal auf dem Server und einmal auf dem Client. Ich denke, dasselbe gilt für den Rückruf, der an useComponentDidMount übergeben wird. useComponentDidMount leitet auf useEffect weiter, um den Aufruf des Rückrufs zu beenden. Bis der Rückruf von useEffect ausgeführt wird, wird die Funktion der Komponente zweimal ausgeführt - einmal auf dem Server und einmal auf dem Client. Ist das nicht der Fall?
Ben Carp

53

Laut reactjs.org wird componentWillMount in Zukunft nicht mehr unterstützt. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

Es ist nicht erforderlich, componentWillMount zu verwenden.

Wenn Sie etwas tun möchten, bevor die Komponente bereitgestellt wurde, tun Sie dies einfach im Konstruktor ().

Wenn Sie Netzwerkanforderungen ausführen möchten, tun Sie dies nicht in componentWillMount. Dies liegt daran, dass dies zu unerwarteten Fehlern führt.

Netzwerkanforderungen können in componentDidMount ausgeführt werden.

Ich hoffe es hilft.


aktualisiert am 08/03/2019

Der Grund, warum Sie nach componentWillMount fragen, liegt wahrscheinlich darin, dass Sie den Status vor dem Rendern initialisieren möchten.

Mach es einfach in useState.

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

oder vielleicht möchten Sie eine Funktion in componentWillMount ausführen, wenn Ihr ursprünglicher Code folgendermaßen aussieht:

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

Mit Hook müssen Sie lediglich die Lebenszyklusmethode entfernen:

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

Ich möchte der ersten Antwort zu useEffect nur etwas hinzufügen.

useEffect(()=>{})

useEffect wird bei jedem Rendering ausgeführt und ist eine Kombination aus componentDidUpdate, componentDidMount und ComponentWillUnmount.

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

Wenn wir in useEffect ein leeres Array hinzufügen, wird es nur ausgeführt, wenn die Komponente bereitgestellt wird. Dies liegt daran, dass useEffect das Array vergleicht, das Sie an es übergeben haben. Es muss also kein leeres Array sein. Es kann sich um ein Array handeln, das sich nicht ändert. Zum Beispiel kann es [1,2,3] oder ['1,2'] sein. useEffect wird immer noch nur ausgeführt, wenn die Komponente bereitgestellt ist.

Es hängt von Ihnen ab, ob es nur einmal oder nach jedem Rendern ausgeführt werden soll. Es ist nicht gefährlich, wenn Sie vergessen haben, ein Array hinzuzufügen, solange Sie wissen, was Sie tun.

Ich habe ein Beispiel für Hook erstellt. Bitte probieren Sie es aus.

https://codesandbox.io/s/kw6xj153wr


aktualisiert am 21/08/2019

Es ist weiß, seit ich die obige Antwort geschrieben habe. Ich denke, Sie müssen auf etwas achten. Wenn Sie verwenden

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

Wenn reag die Werte vergleicht, die Sie an das Array [] übergeben haben, wird Object.is () zum Vergleichen verwendet. Wenn Sie ein Objekt daran übergeben, z

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

Dies ist genau das gleiche wie:

useEffect(()=>{})

Es wird jedes Mal neu gerendert, da beim Vergleichen eines Objekts durch Object.is () dessen Referenz und nicht der Wert selbst verglichen wird. Es ist dasselbe wie der Grund, warum {} === {} false zurückgibt, weil ihre Referenzen unterschiedlich sind. Wenn Sie immer noch das Objekt selbst und nicht die Referenz vergleichen möchten, können Sie Folgendes tun:

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

17
und die Frage war, wie man es mit Haken
umsetzt

3
Sie müssen es jedoch nicht mit Hooks implementieren, da es nicht unterstützt wird. Sie müssen nicht lernen, wie man das mit Haken macht.
MING WU

1
Nachdem Sie erwähnt haben, dass componentDidMount der richtige Lebenszyklus ist, hätten Sie hinzufügen können, wie Sie dies in Ihre Antwort implementieren können, und dann wäre Ihre Antwort sinnvoller als die akzeptierte Antwort
Shubham Khatri

8
Dies sollte sicherlich die akzeptierte Antwort sein - es erklärt, dass ComponentWillMount im Hooks-Paradigma nicht verfügbar ist. Die Initialisierung in Funktionskomponenten wird vereinfacht - sie muss nur Teil der Funktion sein
Shiraz

1
Wie ist das dasselbe wie componentWillMount? Wenn Sie Code in die Funktionskomponente werfen, wird jedes einzelne Rendering ausgeführt, nicht nur, wenn die Komponente bereitgestellt werden soll.
Overcode

13

useLayoutEffectDies könnte mit einer leeren Gruppe von Beobachtern ( []) erreicht werden, wenn die Funktionalität tatsächlich ähnlich ist componentWillMount- sie wird ausgeführt, bevor der erste Inhalt in das DOM gelangt - obwohl es tatsächlich zwei Aktualisierungen gibt, die jedoch synchron sind, bevor sie auf den Bildschirm gezeichnet werden.

beispielsweise:


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

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

Der Vorteil gegenüber useStateeinem Initialisierer / Setter oder useEffectobwohl er möglicherweise einen Render-Durchgang berechnet, gibt es keine tatsächlichen Renderings für das DOM, die ein Benutzer bemerken wird, und es wird vor dem ersten erkennbaren Rendering ausgeführt, was nicht der Fall ist useEffect. Der Nachteil ist natürlich eine leichte Verzögerung beim ersten Rendern, da vor dem Malen auf dem Bildschirm eine Überprüfung / Aktualisierung erfolgen muss. Es hängt jedoch wirklich von Ihrem Anwendungsfall ab.

Ich persönlich denke, useMemoist in einigen Nischenfällen in Ordnung, in denen Sie etwas Schweres tun müssen - solange Sie bedenken, dass dies die Ausnahme gegenüber der Norm ist.


3
useLayoutEffect ist der richtige Weg !!!! Dies beantwortet meine Frage bezüglich der Überprüfung, ob der Benutzer angemeldet ist. (Das Problem war, dass die Komponenten geladen wurden, und dann, ob der Benutzer angemeldet ist.) Meine Frage ist jedoch, ob dies die Standardpraxis ist. Ich sehe nicht an zu vielen Orten
Jessica

1
Ja, es ist ziemlich häufig. wird auch in offiziellen React-Dokumenten erwähnt - nur in kleinerem Text, da das Double-DOM-Rendering Auswirkungen hat, um die Logik auszuführen, bevor ein Benutzer dies bemerkt.
Rob2d

Tatsächlich wird es nach dem Rendern der Komponente ausgeführt. Es ist also völlig anders als componentWillMount.
Jiri Mihal

6

Ich habe einen benutzerdefinierten Hook geschrieben, der eine Funktion vor dem ersten Rendern einmal ausführt.

useBeforeFirstRender.js

import { useState, useEffect } from 'react'

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

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

  if (!hasRendered) {
    fun()
  }
}

Verwendung:

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


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

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

6

So simuliere ich Konstruktoren in Funktionskomponenten mithilfe des useRefHooks:

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

Hier ist das Beispiel für den Lebenszyklus:

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

Natürlich haben Klassenkomponenten keine BodySchritte, es ist aufgrund unterschiedlicher Funktions- und Klassenkonzepte nicht möglich, eine 1: 1-Simulation durchzuführen.


Ich habe mich nicht mit Ihrem Beispiel befasst, aber Ihr erstes Code-Snippet funktioniert für mich. Danke!
SAndriy

3

Es gibt eine schöne Problemumgehung zu implementieren componentDidMountund componentWillUnmountmit useEffect.

useEffectKann basierend auf der Dokumentation eine "Bereinigungs" -Funktion zurückgeben. Diese Funktion wird nicht beim ersten useEffectAufruf aufgerufen, sondern nur bei nachfolgenden Aufrufen.

Wenn wir den useEffectHook ohne Abhängigkeiten verwenden, wird der Hook daher nur aufgerufen, wenn die Komponente bereitgestellt ist, und die Funktion "Bereinigen" wird aufgerufen, wenn die Komponente nicht bereitgestellt wird.

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

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

Der Funktionsaufruf für die Bereinigungsrückgabe wird nur aufgerufen, wenn die Komponente nicht bereitgestellt ist.

Hoffe das hilft.


2
Wie hilft das, wenn es nichts mit componentWillMount zu tun hat ? Vermisse ich etwas
ZenVentzi

Ja, Sie vermissen die Tatsache, dass Sie im selben useEffectAnruf die gleiche Funktionalität componentWillMountund componentWillUnmountauf nette und saubere Weise erhalten
AfikDeri

Das stimmt nicht, wird useEffectnur nach dem Rendern ausgeführt, während es componentWillMountvor dem Rendern der Komponente ausgeführt wird.
Overcode

@Overcode Ich habe componentDidMountnicht darüber gesprochen componentWillMount. Ich habe das in der Frage verpasst, mein schlechtes.
AfikDeri

1

Sie können den useMemo-Hook hacken, um ein componentWillMount-Lebenszyklusereignis zu imitieren. Mach einfach:

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

Sie müssten den useMemo-Hook vor allem behalten, was mit Ihrem Status interagiert. Dies ist nicht beabsichtigt, aber es hat bei allen ComponentWillMount-Problemen funktioniert.

Dies funktioniert, weil useMemo keinen Wert zurückgeben muss und Sie ihn nicht als irgendetwas verwenden müssen, sondern weil es einen Wert speichert, der auf Abhängigkeiten basiert, die nur einmal ausgeführt werden ("[]") und über unserer Komponente liegt Wird einmal ausgeführt, wenn die Komponente vor allem anderen bereitgestellt wird.


0

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

Denken Sie daran, dass die an useMemo übergebene Funktion während des Renderns ausgeführt wird. Tun Sie dort nichts, was Sie normalerweise beim Rendern nicht tun würden. Zum Beispiel gehören Nebenwirkungen zu useEffect, nicht zu useMemo.


usememo dient zur Leistungsoptimierung. Ein Haken wird erneut gerendert, nachdem er bereits montiert wurde, wenn sich eine Requisite ändert, was den Zweck des Autors zunichte macht.
max54

0

Die Antwort von Ben Carp scheint mir nur eine gültige zu sein.

Da wir jedoch funktionale Methoden verwenden, kann nur ein anderer Ansatz von Closure und HoC profitieren:

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

Dann benutze es:

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

0

Kurze Antwort auf Ihre ursprüngliche Frage, wie componentWillMountmit React Hooks verwendet werden kann:

componentWillMountist veraltet und gilt als Vermächtnis . Reagieren Empfehlung :

Im Allgemeinen empfehlen wir, stattdessen den Konstruktor () zum Initialisieren des Status zu verwenden.

In den Hook-FAQ erfahren Sie nun, was das Äquivalent eines Klassenkonstruktors für Funktionskomponenten ist:

Konstruktor: Funktionskomponenten benötigen keinen Konstruktor. Sie können den Status im useState-Aufruf initialisieren. Wenn die Berechnung des Ausgangszustands teuer ist, können Sie eine Funktion an useState übergeben.

Ein Anwendungsbeispiel componentWillMountsieht also so aus:

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

Fügen Sie einfach ein leeres Abhängigkeitsarray in useEffect hinzu, als das es funktioniert componentDidMount.

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

0

Es gibt einen einfachen Trick, um das zu simulieren componentDidMountund Folgendes zu componentWillUnmountverwenden useEffect:

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

  return () => {
    console.log("componentWillUnmount");
  };
}, []);
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.