¿Cómo usar componentWillMount () en React Hooks?

176

En los documentos oficiales de React menciona:

Si está familiarizado con los métodos de ciclo de vida de la clase React, puede pensar en useEffect Hook como componentDidMount, componentDidUpdate y componentWillUnmount combinados.

Mi pregunta es: ¿cómo podemos usar el componentWillMount()método lifecyle en un gancho?

Abrar
fuente

Respuestas:

340

No se puede utilizar cualquiera de los métodos de ciclo de vida existentes ( componentDidMount, componentDidUpdate, componentWillUnmountetc.) en un gancho. Solo se pueden usar en componentes de clase. Y con Hooks solo puede usar en componentes funcionales. La siguiente línea proviene del documento React:

Si está familiarizado con los métodos del ciclo de vida de clase Reaccionar, se puede pensar en useEffectgancho como componentDidMount, componentDidUpdatey componentWillUnmountcombinado.

sugerir es que puede imitar estos métodos de ciclo de vida del componente de clase en componentes funcionales.

El código interno se componentDidMountejecuta una vez cuando el componente está montado. useEffectgancho equivalente para este comportamiento es

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

Observe el segundo parámetro aquí (matriz vacía). Esto se ejecutará solo una vez.

Sin el segundo parámetrouseEffect , se llamará al gancho en cada render del componente que puede ser peligroso.

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

componentWillUnmountse usa para la limpieza (como eliminar oyentes de eventos, cancelar el temporizador, etc.). Supongamos que está agregando un detector de eventos componentDidMounty eliminándolo componentWillUnmountcomo se muestra a continuación.

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

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

El equivalente de gancho del código anterior será el siguiente

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

  // returned function will be called on component unmount 
  return () => {
    window.removeEventListener('mousemove', () => {})
  }
}, [])
Bhaskar Gyan Vardhan
fuente
184
Buena explicación para otros eventos de Lifecycle, pero esta no es la pregunta específica sobre una alternativa a componentWillMount ().
Shiraz
3
En los ejemplos de PanResponder que he visto, componenteWillMount parece ser necesario, de lo contrario, obtendrá panHandlers indefinidos.
Dror Bar
2
Ahora realmente entiendo la useEffect()función, gracias.
Iharob Al Asimi
67
¿Por qué es esta una respuesta aceptada? Usted no ha mencionado un equivalente de gancho paracomponentWillMount
Mykybo
3
@techexpert La pregunta solicitó un equivalente a componentWillMount, no componentWillUnmount. De hecho, esta respuesta no responde a la pregunta, y solo reitera lo que el OP ya implicaba saber.
JoshuaCWebDeveloper
62

useComponentDidMount hook

En la mayoría de los casos useComponentDidMountes la herramienta a utilizar. Se ejecutará solo una vez, después de que el componente se haya montado (renderizado inicial).

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

useComponentWillMount

Es importante tener en cuenta que los componentes en clase componentWillMountse consideran heredados. Si necesita que el código se ejecute solo una vez antes de que se haya montado el componente, puede usar el constructor. Más sobre esto aquí . Dado que el componente funcional no tiene el equivalente de un constructor, usar un gancho para ejecutar código solo una vez antes de los montajes de componentes puede tener sentido en ciertos casos. Puedes lograrlo con un gancho personalizado.

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

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

  willMount.current = false;
};

Sin embargo, hay una trampa. No lo use para establecer asincrónicamente su estado (por ejemplo, después de una solicitud del servidor. Como es de esperar que afecte a la representación inicial que no lo hará). Tales casos deben ser manejados con useComponentDidMount.

Manifestación

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

Ben Carp
fuente
18
Esta es la única respuesta que responde a la pregunta y tiene sentido. ¡Gracias!
chumakoff
3
El único problema con eso es que obtienes un render adicional debido a la actualización de estado involucrada. Al usar una referencia en su lugar, obtienes el comportamiento deseado sin el render extra: `const useComponentWillMount = func => {const willMount = useRef (true); useEffect (() => {willMount.current = false;}, []); if (willMount.current) {func (); }}; `
remix23
2
Esta implementación funcional componentWillMountbasada en useEffecttiene dos problemas. La primera es que no hay un ciclo de vida de montaje en los componentes funcionales, ambos ganchos se ejecutarán después de que el componente se haya procesado, por lo que Runs only once before component mountses engañoso. El segundo es que componentWillMountse llama en la representación del servidor y useEffectno lo es. Muchas bibliotecas aún dependen UNSAFE_componentWillMountporque actualmente es la única forma de activar un efecto secundario en el lado del servidor.
Paolo Moretti
2
@PaoloMoretti, gracias. Este enlace de componentWillMount, no es el equivalente exacto del ciclo de vida de componentWillMount en un componente de clase. Sin embargo, la función que se le pasa se ejecutará de inmediato, solo la primera vez que se llama. Esto prácticamente significa que se ejecutará antes de que se procese, e incluso antes de que devuelva un valor por primera vez. ¿Podemos estar de acuerdo en eso? Estoy de acuerdo en que usar el nombre componentWillMount no es ideal, ya que este nombre tiene cierto significado de la versión del ciclo de vida de la clase. Quizás sea mejor que lo llame "useRunPreMount".
Ben Carp
1
@PaoloMoretti, no lo entiendo del todo. No trabajo con SSR, pero mi comprensión es que en el componente SSR WillMount se ejecuta dos veces, una en el servidor y otra en el cliente. Creo que lo mismo es cierto para la devolución de llamada que se pasa a useComponentDidMount. useComponentDidMount se retransmite en useEffect para dejar de invocar la devolución de llamada. Hasta que se ejecute la devolución de llamada de useEffect, la función del componente se ejecutará dos veces, una en el servidor y otra en el cliente. ¿No es ese el caso?
Ben Carp
53

Según reactjs.org, componentWillMount no será compatible en el futuro. https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

No hay necesidad de usar componentWillMount.

Si desea hacer algo antes de montar el componente, simplemente hágalo en el constructor ().

Si desea hacer solicitudes de red, no lo haga en componentWillMount. Es porque hacer esto conducirá a errores inesperados.

Las solicitudes de red se pueden hacer en componentDidMount.

Espero eso ayude.


actualizado el 08/03/2019

La razón por la que solicita componenteWillMount es probablemente porque desea inicializar el estado antes de los renders.

Solo hazlo en useState.

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

o tal vez desee ejecutar una función en componentWillMount, por ejemplo, si su código original se ve así:

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

con hook, todo lo que necesita hacer es eliminar el método del ciclo de vida:

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

Solo quiero agregar algo a la primera respuesta sobre useEffect.

useEffect(()=>{})

useEffect se ejecuta en cada render, es una combinación de componentDidUpdate, componentDidMount y ComponentWillUnmount.

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

Si agregamos una matriz vacía en useEffect, se ejecuta justo cuando se monta el componente. Esto se debe a que useEffect comparará la matriz que le pasó. Por lo tanto, no tiene que ser una matriz vacía, puede ser una matriz que no cambia. Por ejemplo, puede ser [1,2,3] o ['1,2']. useEffect todavía solo se ejecuta cuando se monta un componente.

Depende de usted si desea que se ejecute solo una vez o después de cada renderizado. No es peligroso si olvidó agregar una matriz siempre que sepa lo que está haciendo.

Creé una muestra para hook. Por favor, míralo.

https://codesandbox.io/s/kw6xj153wr


actualizado el 21/08/2019

Ha sido blanco desde que escribí la respuesta anterior. Hay algo a lo que creo que debes prestarle atención. Cuando usas

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

Cuando reacciona compara los valores que pasó a la matriz [], utiliza Object.is () para comparar. Si le pasa un objeto, como

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

Esto es exactamente lo mismo que:

useEffect(()=>{})

Se volverá a representar cada vez porque cuando Object.is () compara un objeto, compara su referencia, no el valor en sí. Es lo mismo que por qué {} === {} devuelve falso porque sus referencias son diferentes. Si aún desea comparar el objeto en sí, no la referencia, puede hacer algo como esto:

useEffect(()=>{},[JSON.stringify({name:'Tom'})])
MING WU
fuente
17
y la pregunta era cómo implementarlo con ganchos
Shubham Khatri
3
pero no necesita implementarlo con ganchos porque no será compatible. No es necesario aprender a hacer eso con ganchos.
MING WU
1
Ahora que ha mencionado que componentDidMount es el ciclo de vida correcto para usar, podría haber agregado cómo implementar eso en su respuesta y luego su respuesta tendría más sentido que la respuesta aceptada
Shubham Khatri
8
Seguramente esta debería ser la respuesta aceptada: explica que ComponentWillMount no está disponible en el paradigma de ganchos. La inicialización en componentes funcionales se simplifica: solo necesita ser parte de la función
Shiraz
1
¿Cómo es esto lo mismo que componentWillMount? Si arroja código en el componente funcional, ejecutará cada renderizado, no solo cuando el componente esté a punto de montarse.
Overcode
13

useLayoutEffectpodría lograr esto con un conjunto vacío de observadores ( []) si la funcionalidad es realmente similar a componentWillMount: se ejecutará antes de que el primer contenido llegue al DOM, aunque en realidad hay dos actualizaciones pero son sincrónicas antes de dibujar en la pantalla.

por ejemplo:


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

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

El beneficio se obtiene useStatecon un inicializador / configurador o useEffectsi es que puede calcular un pase de renderizado, no hay re-renderizaciones reales al DOM que un usuario notará, y se ejecuta antes del primer renderizado notable, que no es el caso para useEffect. La desventaja es, por supuesto, un ligero retraso en su primer render ya que debe realizarse una verificación / actualización antes de pintar en la pantalla. Sin embargo, realmente depende de su caso de uso.

Pienso personalmente useMemo está bien en algunos casos de nicho en los que necesitas hacer algo pesado, siempre y cuando tengas en cuenta que es la excepción frente a la norma.

rob2d
fuente
3
useLayoutEffect es el camino a seguir !!!! Esto responde a mi pregunta con respecto a verificar si el usuario ha iniciado sesión. (El problema era que los componentes se cargarían y luego verificar si el usuario había iniciado sesión). Mi pregunta es, ¿es esta práctica estándar? No estoy viendo en muchos lugares
Jessica
1
sí, es bastante común; también se menciona en los documentos oficiales de React, solo en texto más pequeño debido a las ramificaciones del render doble DOM para ejecutar la lógica antes de que un usuario se dé cuenta.
rob2d
En realidad, se ejecuta después de representar el componente. Por lo tanto, es totalmente diferente a componentWillMount.
Jiri Mihal
7

Así es como simulo constructor en componentes funcionales usando el useRefgancho:

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

Aquí está el ejemplo del ciclo de vida:

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

Por supuesto, los componentes de la clase no tienen Bodypasos, no es posible hacer una simulación 1: 1 debido a los diferentes conceptos de funciones y clases.

Jiri Mihal
fuente
No me sumergí en su ejemplo, pero su primer fragmento de código funciona para mí, ¡Gracias!
SAndriy
6

Escribí un enlace personalizado que ejecutará una función una vez antes del primer render.

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>
  )
}
Poco tiempo
fuente
3

Hay una buena solución para implementar componentDidMounty componentWillUnmountcon useEffect.

Según la documentación, useEffectpuede devolver una función de "limpieza". Esta función no se invocará en la primera useEffectllamada, solo en las siguientes.

Por lo tanto, si usamos el useEffectgancho sin dependencias, se llamará al gancho solo cuando el componente esté montado y la función de "limpieza" se llamará cuando el componente esté desmontado.

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

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

La llamada a la función de devolución de limpieza se invoca solo cuando el componente está desmontado.

Espero que esto ayude.

AfikDeri
fuente
2
¿Cómo ayuda esto si no tiene nada que ver con componentWillMount ? ¿Me estoy perdiendo de algo?
ZenVentzi
Sí, te estás perdiendo el hecho de que en la misma useEffectllamada obtienes la misma funcionalidad decomponentWillMount y componentWillUnmountde una manera agradable y limpia
AfikDeri
Eso no es cierto, useEffect solo se ejecuta después de un render mientras se componentWillMountejecuta antes de que el componente rinda.
Overcode
@Overcode del que estaba hablando componentDidMountno componentWillMount. Eché de menos eso en la pregunta, mi mal.
AfikDeri
1

Puede hackear el enlace useMemo para imitar un evento de ciclo de vida componentWillMount. Solo haz:

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

Debería mantener el gancho useMemo antes de cualquier cosa que interactúe con su estado. No es así como se pretende, pero funcionó para mí para todos los problemas de componentWillMount.

Esto funciona porque useMemo no requiere devolver un valor y no tiene que usarlo como algo, pero como memoriza un valor basado en dependencias que solo se ejecutará una vez ("[]") y está encima de nuestro componente. se ejecuta una vez cuando el componente se monta antes que nada.

jpmarks
fuente
0

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

Recuerde que la función pasada a useMemo se ejecuta durante el renderizado. No hagas nada allí que normalmente no harías mientras renderizas. Por ejemplo, los efectos secundarios pertenecen a useEffect, no useMemo.


fuente
usememo es para la optimización del rendimiento. Se volverá a generar un gancho después de haber sido montado si se cambia un accesorio, lo que vence el propósito del autor.
max54
0

La respuesta de Ben Carp me parece válida.

Pero dado que estamos utilizando formas funcionales, solo otro enfoque puede beneficiarse del cierre y HoC:

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

Entonces úsalo:

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

Respuesta corta a su pregunta original , cómo componentWillMountse puede usar con React Hooks:

componentWillMountestá en desuso y se considera legado . Reaccionar recomendación :

En general, recomendamos usar el constructor () en su lugar para inicializar el estado.

Ahora, en las Preguntas frecuentes de Hook , descubrirá cuál es el equivalente de un constructor de clase para componentes de funciones:

constructor: los componentes de función no necesitan un constructor. Puede inicializar el estado en la llamada useState. Si calcular el estado inicial es costoso, puede pasar una función para usar State.

Entonces, un ejemplo de uso se componentWillMountve así:

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
fuente
0

Simplemente agregue una matriz de dependencia vacía en useEffect, funcionará como componentDidMount.

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

Hay un truco simple para simular componentDidMounty componentWillUnmountusando useEffect:

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

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