Comprender cómo funcionan los métodos del ciclo de vida en componentes creados con clases es fundamental para evitar bucles infinitos y controlar correctamente el flujo de renderizado en React. Aquí se explica paso a paso cómo replicar el comportamiento de los efectos usando estos métodos, cuándo se ejecutan y por qué los condicionales son indispensables.
¿Cómo se crea un estado de carga en un componente de clase?
En los componentes creados con clases, no es posible declarar múltiples llamadas a useState. En su lugar, todas las propiedades del estado viven dentro de un único objeto this.state. Para agregar un estado de carga, se añade la propiedad loading con valor false por defecto dentro del constructor [01:00].
Cuando el usuario hace clic en el botón de comprobar, se llama a this.setState({ loading: true }), lo que actualiza el estado y muestra un mensaje de "Cargando" condicionado a this.state.loading === true [02:06].
¿Qué son los métodos del ciclo de vida y cuándo se ejecutan?
Los métodos del ciclo de vida son funciones que React invoca automáticamente en momentos específicos del renderizado de un componente de clase. Los principales son:
componentWillMount: se ejecuta antes de renderizar el componente por primera vez. Su nombre en inglés indica futuro: will mount, se va a montar [04:10].
componentDidMount: se ejecuta después de que el componente ya fue renderizado. Did indica pasado en inglés: ya se montó [04:30].
componentWillUnmount: se ejecuta justo antes de que el componente sea desmontado, es decir, cuando desaparece del DOM [04:45].
El orden de ejecución no depende de dónde se escriban en el código, sino del momento del ciclo que representan. React siempre los ejecuta en su orden lógico.
¿Por qué componentWillMount ya no es recomendable?
Al usar componentWillMount, React muestra un warning indicando que este método fue renombrado y ya no se recomienda [06:12]. Siguiendo los principios de diseño de React, en lugar de eliminar funcionalidades de golpe, el equipo de React primero las renombra, muestra alertas y sugiere alternativas. En este caso, el método pasa a llamarse UNSAFE_componentWillMount, dejando claro que su uso es inseguro y que la alternativa preferida son los efectos con hooks [07:00].
¿Cómo se detecta el desmontaje de un componente?
Para observar componentWillUnmount en acción, se crea un componente separado llamado Loading que solo renderiza un párrafo con el texto "Cargando". Cuando el estado loading pasa de true a false, el componente Loading se desmonta y se ejecuta su método componentWillUnmount, imprimiendo un mensaje en consola [09:15].
¿Cómo evitar bucles infinitos con componentDidUpdate?
El método componentDidUpdate se ejecuta cada vez que hay un cambio en el estado, pero no en el renderizado inicial [11:00]. Esto lo diferencia de componentDidMount, que solo corre una vez al montar.
El peligro aparece al llamar this.setState dentro de componentDidUpdate sin protección. Si se ejecuta this.setState({ loading: false }) directamente, ocurre lo siguiente:
setState provoca una nueva actualización.
La nueva actualización vuelve a ejecutar componentDidUpdate.
componentDidUpdate vuelve a llamar a setState.
Se genera un bucle infinito [13:20].
La solución es un condicional que valide el estado antes de actuar:
Cuando loading es true, se programa un setTimeout que después de tres segundos lo cambia a false. Eso dispara otra ejecución de componentDidUpdate, pero como loading ya es false, el condicional no pasa y el ciclo se detiene [14:50].
Esta lógica es equivalente a la protección que se usa en los efectos con hooks, donde el segundo argumento del array de dependencias controla cuándo se vuelve a ejecutar.
Los métodos del ciclo de vida resultan más descriptivos que los efectos, donde todo depende de un array como segundo argumento. Sin embargo, ambos requieren entender bien qué significa montar, actualizar y desmontar para evitar comportamientos inesperados. Si te interesa profundizar en optimización de render en React, comparte tu interés en los comentarios.
Para complementar esta clase y por qué hay métodos que se dejaron de usar?
.
Antes React utilizaba muchos más metodos para actualizar el estado (siguiente imagen) pero en algun momento a lo mejor se dieron cuenta que era muy complicado porque introducía mucha mas complejidad, codigo repetido y causaba mucha confusión debido a que habían muchos metodos similares, dejo el ciclo de vida con todos los métodos utilizados anetiormente a continuación:
.
Ahora este ciclo de vida se ha reducido y ya no hay tantos pasos para hacer una actualización de estado, por lo que es sencillo entender mas fácilmente cómo se actualiza React, ya no hay tantos pasos intermedios y ya no tendríamos que escribir tanto código si siguieramos utilizando las clases para escribir código. Ya que se han marcado que ya no se deberían usar algunos métodos. Este es el ciclo de vida que se usa ahora:
.
Ahora bien useEffect() recoge lo que hace componentDidMount(), componentDidUpdate(), y componentWillUnmount(), es decir, que useEffect() se encarga de ejecutar un función cuando nuestro componente se va a mostrar ya sea por una actualización o sencillamente porque es la primera vez que se va a mostrar. Si nos fijamos bien componentWillUnmount() es la encargada de eliminar el componente de nuestra interfaz de usuario pues bien con useEffect() tambien se puede utilizar para ejecutar una función una vez nuestro componente se vaya a eliminar esto es útli para: eliminar eventos, hacer una petición al servidor para guardar algo o limpiar algun temporizador. Aunque no se si sea la historia oficial de React 😛 imagino que eso fue lo que pensaron los ingenieros.
.
Pues bien para hacer una comparación de lo que trabajar con clases y useEffect tenemos tres opciones:
.
componentDidMount() Ejecutar algo una vez el componente se inicializa:
useEffect(()=>{// Algo que se ejecuta dentro},[]);//Los corchetes para indicar que se ejecute solo la primera vez
.
componentDidUpdate() Ejecutar algo en alguna actualización del estado:
useEffect(()=>{// Algo que se ejecuta dentro},[dependencia]);//Se ejecuta cada vez que la dependencia cambia
componentWillUnmount() Ejecuta algo cuando el componente se va a dejar de mostrar
useEffect(()=>{window.addEventListener('mousemove',()=>{});// Una función que se ejecuta despues de que el componente se eliminereturn()=>{window.removeEventListener('mousemove',()=>{})//Elimina el evento}},[]);
.
Por último añadir que no se debería usar useEffect sin el arreglo de dependencias (el que se pone al final). Ya que estaríamos ejecutando todo lo que hay dentro en cada nuevo render por lo que agregaríamos calculos que a lo mejor nos podríamos ahorrar.
.
Eso fue todo, espero que te haya gustadoNunca pares de aprender 💚
Excelente aporte, es verdad que se hacia muy complicado tener tantos pasos intermedios, ahora es mucho mas sencillo y el codigo es mas facil de mantener
Muy bueno tu aporte, si ahora es muchas mas fácil de entender sin tantos pasos intermedios. :)
Comparar el useEffect hook con los Lifecycle de los class components fue la mejor manera para por fin entender este hook. Excelente clase 😄
.
Les comparto este recurso que me ayudo mucho para complementar mas el contenido de esta clase: Replacing Lifecycle methods with React Hooks
Nice example
💚 Corazoncito si prefieres UseState 😅.
.
Ya en serio, las clases en React son bastante más difíciles de entender, sin embargo al hacer consultas en internet me he encontrado con muchos sitios donde las usan, así que toca aprenderlas de arriba a abajo.
nuestra trabajo sera actualizarlas a hooks!
No es necesario actualizarlas, porque hay algunas cosas que no se pueden hacer con hooks (o al menos no todavía). Es necesario entender ambas formas y usar la mejor opción en el momento adecuado.
Donde le puedo dar like a esta clase? Me gustó mucho🥹
importReactfrom"react";import{Loading}from"./Loading";classClassStateextendsReact.Component{constructor(props){super(props);this.state={error:true,loading:false,};}UNSAFE_componentWillMount(){console.log("UNSAFE_componentWillMount");}componentDidMount(){console.log("componentDidMount");}componentDidUpdate(){console.log("Update");if(!!this.state.loading){setTimeout(()=>{console.log("Doing the validation");this.setState({loading:false});console.log("Finishing the validation");},3000);}}render(){return(<div><h3>Delete{this.props.name}</h3><p>Please enter the security code</p>{this.state.error&&(<p>Error:Security code is incorrect</p>)}{this.state.loading&&(<Loading/>)}<input placeholder="Security Code"/><button
onClick={()=>this.setState({loading:true})}>Check</button></div>);}}export{ClassState};
Este metodo esta deprecado componentWillMount dejara de funcionar con la actualizacion de React 17
Ya esta pasando U.u
Los ejercicios los estoy haciendo sobre React 18 y aún me permite utilizar el componentWillMount. Sin embargo estás en lo correcto de que se encuentra deprecado, y de por sí en la documentación de React hacen énfasis en que está obsoleto y su uso se debe evitar de cualquier manera
componentDidUpdate se usa para realizar una accion con la actualizacion del componente equivalente a useEffects sin segundo parametro.
Además tenemos componentDidMounted con su equivalente useEffects con [] como segundo parametro.
¿Pero que sucede con useEffects con segundo parametro de arreglo no vacio, cual es su equivalente en clases?
No tiene un equivalente tal cual. Solo se ejecuta cada vez que hay cambios en un estado en específico (contando su inicialización también como cambio para reaccionar). Con clases podemos replicar este comportamiento usando condicionales y validaciones del nuevo estado vs. el viejo dentro de los métodos de ciclo de vida.
Yo se que los Effects son buenos pero, creo que me gustaba más la manera de component, era un poco más clara de pronto es porque aún no comprendo al 100 % los Effect
Bueno saber esto porque en algun momento nos vamos a topar con legacy code
Es un problema cuando a Juan le agarra la chiripiorca: no se le entiende mas nada...
1. componentWillMount: Este método se ejecuta justo antes de que el componente se monte en el DOM o la interfaz de usuario. Es importante mencionar que este método está obsoleto y no se recomienda su uso en nuevas implementaciones. En su lugar, se recomienda utilizar el constructor para inicializar el estado y componentDidMount para las llamadas a la API o las suscripciones.
2. componentDidMount: Este método se ejecuta inmediatamente después de que el componente se monta en el DOM. Es un buen lugar para realizar llamadas a la API, establecer suscripciones o realizar cualquier tipo de inicialización que requiera interacción con el DOM o datos externos.
3. componentWillUnmount: Este método se ejecuta justo antes de que el componente se desmonte y se destruya. Es un buen lugar para realizar la limpieza necesaria, como invalidar temporizadores, cancelar solicitudes de red o eliminar suscripciones.
☯ Comparto mis apuntes sobre Estado y ciclo de vida con React.Component en el siguiente notion
Y recuerden... No siempre vamos a encontrarnos con código moderno y actualizado, entonces siempre es bueno aprender otras formas de desarrollar.
Utilicemos más métodos del ciclo de vida para lograr exactamente el mismo comportamiento y salidas del useEffect por completo. Es decir, hagamos su ejecución inicial (componentDidMount), su validación de nueva ejecución respecto al estado "loading" (shouldComponentUpdate, componentDidUpdate), la limpieza del temporizador (componentWillUnmount y componentDidUpdate), y sus posteriores ejecuciones (componentDidUpdate).
Primero, veamos cómo se comporta el useEffect. Para ello, trabajaremos con el código del curso solo agregándole una función de return para "limpiar el efecto" y algunos console.log para ver en consola las salidas.
Y ahora, ¿Cómo logramos la misma salida con las clases?
Su primera ejecución.
Como sabemos, los efectos se ejecutarán siempre al menos una vez, en el montaje del componente. En las clases logramos esto con el método componentDidMount. Por tanto, nuestro método tendrá exactamente el mismo cuerpo que el useEffect:
componentDidMount(){// First timeconsole.log("Beginning Component Mounting");if(this.state.loading){this.timeoutId=setTimeout(()=>{console.log("Beginning Validation");this.setState({loading:false});console.log("Ending Validation");},3000);}console.log("Ending Component Mounting");}
Las salidas se cambiaron para diferenciarlas del efecto.
Cabe resaltar que en este caso creamos este método solo para explicación y por ver las salidas que vemos inicialmente en el efecto. Realmente podríamos omitirlo y tendríamos la misma funcionalidad.
Si ejecutamos hasta este punto, tendríamos en consola, lo siguiente:
Sigamos el orden de construcción del efecto y hagamos ahora su función de retorno, conocida como función de limpieza.
Para ello, en las clases contamos con el método componentWillUnmount. El código de este método es el mismo que la función de limpieza del efecto:
Sin embargo, este método casi que nunca (por no decir que nunca xD) se ejecutará, ya que solo lo haría cuando se vaya a desmontar el componente perse.
Es por eso que el profesor lo puso en otro componente que si se desmonta entre renders.
Pero entonces si no se ejecuta este método, ¿Cómo podríamos limpiar lo del render anterior?
Bueno, para ello, necesitamos limpiar después de que se actualiza el componente. Y como sabemos, el método del ciclo de vida que se ejecuta cuando el componente se ha actualizado es: componentDidUpdate. Su código, inicialmente sería:
componentDidUpdate(){// Clean up after update (simulate unmount [return function of the effect])clearTimeout(this.timeoutId);console.log("Cleaning After Component Updating");}
Seguramente ya te has dado cuenta, ¡estamos duplicando código! Este es uno de los problemas de los métodos del ciclo de vida.
Probablemente con esto te hayas dado cuenta de que debemos repetir otro código... ¡Exacto! El cuerpo del efecto para que se ejecute en nuevas actualizaciones. Pero antes de eso, veamos cómo podemos evitar una nueva actualización en los componentes de clase.
Con el método shouldComponentUpdate podemos especificar si queremos que un componente se vuelve a actualizar o no, retornando un true o false, respectivamente:
shouldComponentUpdate(nextProps, nextState){// Tip: We can bypass this method by doing the same comparison in the componentDidUpdate methodif(nextState.loading!==this.state.loading)returntrue;returnfalse;}
El problema de este enfoque es que si tuviésemos más props o estados que cambian, deberíamos condicionarlas o sino, el componente solo se actualiza cuando la variable de estado "loading" haya cambiado.
Solo se muestra este método para su conocimiento. React no utiliza este enfoque para evitar aplicar el efecto si ciertos valores (los especificados en el array de dependencias del efecto) no han cambiado. En su lugar, utiliza el método componentDidUpdate para dicha comparación.
**
Ya que conocemos 4 métodos del ciclo de vida, y que hemos logrado el comportamiento de los efectos en su primera ejecución y consideraciones para nuevas ejecuciones, veamos cómo el método componentDidUpdate se encarga de aplicar el efecto en cada nueva actualización.
Copiamos el código del efecto que ya hemos copiado al método componentDidMount al método que se ejecuta después de cada actualización, componentDidUpdate:
componentDidUpdate(){// Clean up after update (simulate unmount [return function of the effect])clearTimeout(this.timeoutId);console.log("Cleaning After Component Updating");// New updates (we have to duplicate the code between CDidMount and CDidUpdate)console.log("Beginning Component Updating");if(this.state.loading){this.timeoutId=setTimeout(()=>{console.log("Beginning Validation");this.setState({loading:false});console.log("Ending Validation");},3000);}console.log("Ending Component Updating");}
Hemos cambiado las salidas para diferenciar del método componentDidMount.
Vemos una vez más que tuvimos que duplicar código para manejar un mismo comportamiento entre renders. Por este y otros motivos, surgen los Hooks.
--
Hasta aquí no hemos visto más la consola, pero si ejecutamos ahora nuestro código, veremos que tendremos la misma estructura de salida que con el useEffect al presionar el botón que cambia el estado de "loading" y esperar la ejecución del cuerpo del setTimeout. La consola nos daría el siguiente resultado si probamos los dos componentes (Omitiendo la ejecución inicial):
Antes de terminar, hagamos la validación que se hace en el efecto para decirle a React que omita el aplicar el efecto si ciertos valores no han cambiado entre renders. Esta es la misma que hicimos en el método shouldComponentUpdate pero React la hace en el método componentDidUpdate. Así que eliminamos el shouldComponentUpdate y el código de la clase completa quedaría de la siguiente forma:
exportclassClassStateextendsReact.Component{constructor(props){super(props);this.state={error:false,loading:false,};}componentDidMount(){// First timeconsole.log("Beginning Component Mounting");if(this.state.loading){this.timeoutId=setTimeout(()=>{console.log("Beginning Validation");this.setState({loading:false});console.log("Ending Validation");},3000);}console.log("Ending Component Mounting");}componentDidUpdate(prevProps, prevState){// Clean up after update (simulate unmount [return function of the effect])clearTimeout(this.timeoutId);console.log("Cleaning After Component Updating");// New updates (we have to duplicate the code between CDidMount and CDidUpdate)if(prevState.loading!==this.state.loading){console.log("Beginning Component Updating");if(this.state.loading){this.timeoutId=setTimeout(()=>{console.log("Beginning Validation");this.setState({loading:false});console.log("Ending Validation");},3000);}console.log("Ending Component Updating");}}componentWillUnmount(){clearTimeout(this.timeoutId);console.log("Cleaning Component Desmounting");}render(){return(<div><h3>Delete{this.props.name}</h3><p>Please, write the security code.</p>{this.state.error&&<p>Error: security code incorrect</p>}{this.state.loading&&<p>Loading...</p>}<input placeholder="Security code"/><button onClick={()=>this.setState({loading:true})}>Check</button></div>);}}
No se olviden que no podemos usar funciones expresivas(anónimas o funciones flechas) en componentes con clases.
Mi código para los componentes de clases:
importReact,{Fragment}from'react';import{Loading}from'./Loading';classClassStateextendsReact.Component{constructor(props){super(props);this.state={error:false,loading:false,};};componentDidMount(){console.log('iniciando ciclo de vida del componente');}componentDidUpdate(){this.state.loading?setTimeout(()=>{this.setState({error:true})this.setState({loading:false})setTimeout(()=>{this.setState({error:false})},3000);},2000):console.log('Validacion Finalizado.')}render(){return(<Fragment><h3>Eliminar{this.props.name}</h3><p>Por favor, escribe el codigo de seguridad.</p><input type="text" placeholder='Codigo de seguridad'/><button
onClick={()=>this.setState({loading:true})} className='btn btn-secondary'>Comprobar</button>{this.state.loading&&<Loading/>}{this.state.error&&<p className='bg-danger text-white'>Error: el codigo no es valido.</p>}</Fragment>)}}export{ClassState};
Aquí una explicación sobre UseEffect, Dan Abramov, vale todo el tiempo que dediques a leerlo.
Yo preferiría el hook de useEffect, creo que es mas facil de comprender
Que es componentWillMount?
componentWillMount era un método del ciclo de vida en componentes de clase de React que se ejecutaba justo antes de que el componente se montara en el DOM. Sin embargo, ya no se recomienda su uso y ha sido renombrado a UNSAFE_componentWillMount. React aconseja usar useEffect en componentes funcionales o componentDidMount en componentes de clase para la mayoría de los casos.
Por favor, curso de ¡Optimización de Render en React!
Me parece que los métodos de ciclo de vida en React.Component, incluso pueden servir para poder hacer lazy loading :)