Crea una cuenta o inicia sesión

¡Continúa aprendiendo sin ningún costo! Únete y comienza a potenciar tu carrera

Efectos con useEffect

4/19
Recursos

Aportes 29

Preguntas 7

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Yo si quiero un curso de optimización de render en react 🙋🏾‍♀️

React.useEffect es un método con 2 parámetros:

  1. El primero, siempre se usa y es una función a ejecutar.

  2. El segundo, opcional e importante, nos indica cuando se va a ejecutar nuestro primer parámetro. Los posibles valores de este parámetro son:

    1. Ningun valor:
      esta función se ejecutará cada vez que nuestro componente haga render (es decir, cada vez que haya cambios en cualquiera de nuestros estados).

    2. Arreglo vacio:
      nuestro efecto solo se ejecuta una vez, cuando recién hacemos el primer render de nuestro componente.

    3. Arreglo no vacio:
      O también podemos enviar un array con distintos elementos para decirle a nuestro efecto que no solo ejecute en el primer render, sino también cuando haya cambios en esos elementos del array.

El React.useEffect recibe dos parámetros: el primer argumento es una función y el segundo argumento nos dice cuando se va a ejecutar el primer argumento.

React.useEffect(() => {}, []);

Valores del segundo argumento

  1. Array vacio. El código se ejecuta cuando se renderice por primera vez el componente.
  2. Ningún valor. El código se ejecuta cada vez que se haga render de nuestro componente o, dicho de de otra forma, cada vez que se modifique un estado.
  3. Array con elementos. Los estados que le pasemos al array harán que cuando haya un cambio en ellos se ejecutará la función.

Resumen de la clase

  • Ahora trabajaremos con los efectos en funciones y con su equivalente en React.component que son los métodos de ciclo de vida.
  • En esta clase nos enfocaremos en los efectos del componente UseState

Situacion

  • Queremos que después de 2 segundos que nuestro estado loading cambie a true por el botón vuelva a ser false.
  • No podemos simplemente escribir if(loading) setLoading(false) pues el código se va a ejecutar siempre que React haga un render del componente.
  • El render del componente se va activar con el cambio de cualquier cambio de estado.
  • Cuando se vuelve a renderizar, los estados vuelven a ser creados pero no serán el valor inicial sino el ultimo estado definido con los actualizadores

Solución
Para poder ejecutar nuestro código no en cada render sino bajo ciertas condiciones utilizamos el React.useEffects

Que clase mas cool 😎😎😎

import React from "react";

function UseState({ name }) {
  const [error, setError] = React.useState(true);
  const [loading, setLoading] = React.useState(false);

  React.useEffect(() => {
    console.log("Starting the effect");

    if(!!loading) {
      setTimeout(() => {
        console.log("Doing the validation");

        setLoading(false);

        console.log("Finishing the validation");
      }, 3000);
    }

    console.log("Finishing the effect");
  }, [loading]);

  return (
    <div>
      <h2>Delete {name}</h2>

      <p>Please enter the security code</p>

      {error && (
        <p>Error: Security code is incorrect</p>
      )}
      {loading && (
        <p>Loading...</p>
      )}

      <input placeholder="Security Code" />

      <button
        onClick={() => setLoading(true)}
      >Check</button>
    </div>
  );
}

También pueden usar async/await, para hacer el código más legible:

    useEffect(() => {
        const fetching = async () => {
            console.log("Iniciando validación");
            await sleep(1000);
            setLoading(false);
            console.log("Terminando validación");
        }
        if(!!loading){
            fetching();
        }
    }, [loading]);

Donde la función sleep es:

const sleep = async (ms) => {
    return new Promise(resolve => setTimeout(resolve, ms));
}

Pueden probar este código para revisar mas ciclos de vida, si vienen de class components o de otros framework como Angular o Vue depronto puedan relacionarlo así.
Cómo aporte adicional, como pueden ver estoy retornando una array function, esta se ejecutara cuando el componente se desmonte y cuando pase al siguiente render respectivamente. Creo que implementando el codigo y tratando de revisarlo en la consola puede quedar mas claro.

useEffect(() => {
    console.log('on component mount');
    return () => {
      console.log('on component unmount');
    }
  }, []);
  useEffect(() => {
    console.log('on component update/render');
    return () => {
      console.log('on component update/render unmount');
    }
  });

El useEffect es un hook que realiza efectos secundarios o side effects.
Un efecto secundario es una acción externa al código que se ejecuta: llamadas a API, suscripciones a eventos, actualización del DOM.

Recibe dos argumentos:

  1. la función a ejecutar
  2. un array de dependencias que determina cuándo se va a ejecutar nuestro efecto. O puede no recibir nada
    2.1) Si no recibe nada se ejecuta al primer y cada renderizado de nuestra app
    2.2) Array vacío: en el primer renderizado de nuestra app
    2.3) Array con dependencia: se ejecutará cuando se modifique dicha dependencia

3️⃣ Efectos con useEffect

Ahora trabajaremos con los efectos en funciones y con su equivalente en React.component que son los métodos de ciclo de vida

En esta clase nos enfocaremos en los efectos del componente UseState

Situacion

Queremos que después de 2 segundos que nuestro estado loading cambie a true por el botón vuelva a ser false.

No podemos simplemente escribir:

if(loading){
    setLoading(false);
}

Pues el código se va a ejecutar siempre que React haga un render del componente.
El render del componente se va activar con el cambio de cualquier cambio de estado.
Cuando se vuelve a renderizar, los estados vuelven a ser creados pero no serán el valor inicial sino el ultimo estado definido con los actualizadores

Solución

Para poder ejecutar nuestro código no en cada render sino bajo ciertas condiciones utilizamos el React.useEffects

React.useEffect es un método con 2 parámetros:

React.useEffect(() => {
    //...
},[]);
  1. El primero, siempre se usa y es una función a ejecutar.
  2. El segundo, opcional e importante, nos indica cuando se va a ejecutar nuestro primer parámetro. Los posibles valores de este parámetro son:
    • Ningun valor: esta función se ejecutará cada vez que nuestro componente haga render (es decir, cada vez que haya cambios en cualquiera de nuestros estados).
    • Arreglo vacio: **[]** nuestro efecto solo se ejecuta una vez, cuando recién hacemos el primer render de nuestro componente.
    • Arreglo no vacio: [state,error] O también podemos enviar un array con distintos elementos para decirle a nuestro efecto que no solo ejecute en el primer render, sino también cuando haya cambios en esos elementos del array.

termine el código tal cual pero me renderiza dos veces es normal??

Asombroso, por fin pude entender a useEffect y como mandar o no a llamar la lógica que hay dentro de el. Gracias Maestro :D

Creo que ahora le entiendo mejor el uso a los effects.
Ejecutar un bloque de codigo solo cuando cambia una variable

yo quiero también un curso de optimización de render

Que bueno esta vez lo entendi perfectamente!! muchas gracias

Lo que realicé fue agregar una función para cuando se presione clic en el botón de comprobar el loading se vuelva true y dentro de useEffect agregue el callback de setTimeout en 2s para modificar el loading a false y el error a true para que muestre como falso, pero agregue un nuevo setTimeout de 3s para volver a colocar al error a false y todo quede como al principio. 😉

import React, { Fragment, useEffect, useState } from 'react';

function UseState(props){
  const [error, setError] = useState(false);
  const [loading, setLoading] = useState(false);

  const handleClick = () => {
    setLoading(true)
  }

  useEffect(() => {
    console.log('ejecutando useEffect')
      loading ?
      setTimeout(() => {
        setLoading(false);
        setError(true)
        setTimeout(() => {
          setError(false)
        }, 3000);
      }, 2000) :
      console.log('Validacion Finalizado.')
  }, [handleClick]);
  
  return (
    <Fragment>
      <h2>Eliminar {props.name}</h2>
      <p>Por favor, escribe el codigo de seguridad.</p>
      <input type="text" placeholder='Codigo de seguridad' />
      <button
        onClick={() => handleClick()}
      >
        Comprobar</button>
      {loading && <p className='bg-success'>Validando ...</p>}

      {error && <p className='bg-danger text-white'>Error: el codigo no es valido.</p>}
    </Fragment>
  );
}

export { UseState };

Presente mi primera entrevista y me preguntaron sobre optimizacion de render en react

El useState Hook de React nos permite rastrear el estado en un componente dentro de una función.

Al igual que con useState, pueden modificar el import de la siguiente forma:

import React, { useEffect } from "react";

y usar useEffect así:

useEffect(() => {
        console.log("Empezando el efecto");

        if (!!loading) {
			setTimeout(() => {
				console.log("Haciendo la Validación");
	
				setLoading(false);
	
				console.log("Terminando la validación");
	
			}, 3000);
		}
        console.log("Terminando");
    }, [loading]);
El modo estricto en React (Strict Mode) se utiliza para ayudar a identificar problemas potenciales en la aplicación. Cuando desarrollas en este modo, React ejecuta los efectos y componentes dos veces en desarrollo, pero solo en el primer render. Esto es intencional y ayuda a detectar efectos secundarios no deseados, como actualizaciones de estado inesperadas. Este comportamiento no ocurre en producción. Si ves el mensaje dos veces, es probable que estés utilizando efectos que no están correctamente manejados. Asegúrate de revisar tu lógica de efectos y estado. Puedes seguir explorando el manejo de efectos y sus interacciones en la clase de "Efectos con useEffect" de tu curso de React.js.
¡Qué interesante... siempre me pregunté como podría evitar la primera ejecución del useEffect!

Efectos con useEffect

.
Trabajar con efectos en componentes tipo función sería equivalente a trabajar con los métodos del ciclo de vida con los componentes de tipo clase.
.
Lo que se hizo fue crear un estado para loading con su correspondiente actualizador setLoading. Por defecto este estados será false, es decir que no estamos cargando.
.
En el botón de Comprobar vamos a llamar a una función flecha que se encargará de settear nuestro estado de carga en true.
.
Al implementar un useEffect lo que definimos se el primer parámetro o setup que consiste en una función o lógica que queremos efectuar cuando determinadas condiciones aparezcan. Estas condiciones las determinan el segundo parámetro o dependencies que para este caso es un array de dependencias con solo el estado de loading.
.
Este segundo parámetro es opcional y puede incluso tener diferentes valores:
.

  • Ningun valor: Significa que la función se ejecutara cada vez que se perciban cambios en cualquiera de los estados o se renderice el componente. Cada vez que hay un cambio en los estados el componente vuelve a renderizar.
    .
  • Array de dependencias vacío: La función solo se ejecuta una vez, cuando renderizamos por primera vez nuestro componente.
    .
  • Array de dependencias no vacío: La función se ejecutará todas las veces que se perciban cambios en alguno de los elementos dentro de nuestro array, por lo general vienen siendo estados. Además también se ejecutará la primera vez que rendericemos nuestro componentes, pero ahí depende de nosotros si queremos controlar ese comportamiento con alguna condicional dentro del setup o función.

.
Entonces dentro del setup queremos que después de 3 segundos cada vez que el estado de loading cambie podamos settear su estado a false, pero será necesario hacer un control por medio de un condicional que nos permita cambiar de estado a false solo y si loading es verdadero. Esto para evitar que la función se ejecute innecesariamente si ya el estado fuese false.
.

function UseState({ name }) {
  const [error, setError] = React.useState(true);
  const [loading, setLoading] = React.useState(false);

  React.useEffect(() => {
    console.log("Empezando el efecto")

    if (!!loading) {
      setTimeout(() => {
        console.log("Haciendo la validación")
  
        setLoading(false);
        
        console.log("terminando la validación")
      }, 3000);
    }
    
    console.log("Terminando el efecto")
  }, [loading]);
  
  return (
    <div>
      ...
      {loading && (
        <p>Cargando...</p>
      )}

      <input placeholder="Código de seguridad" />
      <button
        onClick={() => setLoading(true)}
      >Comprobar</button>
    </div>
  );
}

UseEffect

useEffect(setup, dependencies?)

.
Los parámetros principales de este hook son:
.

  • Setup: Es la función con la lógica de tu efecto. Su función de configuración también puede devolver opcionalmente una función de limpieza. Cuando su componente se agregue al DOM, React ejecutará su función de configuración. Después de cada renderizado con dependencias modificadas, React primero ejecutará la función de limpieza (si la proporcionó) con los valores anteriores y luego ejecutará su función de configuración con los nuevos valores. Después de que su componente se elimine del DOM, React ejecutará su función de limpieza.
    .

  • Dependencies (opcional): La lista de todos los valores reactivos a los que se hace referencia dentro del código de configuración. Los valores reactivos incluyen props, estado y todas las variables y funciones declaradas directamente dentro del cuerpo del componente. Si su linter está configurado para React, verificará que cada valor reactivo esté especificado correctamente como una dependencia. La lista de dependencias debe tener un número constante de elementos y estar escrita en línea como [dep1, dep2, dep3]. React comparará cada dependencia con su valor anterior utilizando la comparación Object.is. Si omite este argumento, su efecto se volverá a ejecutar después de cada re-render del componente.

.
La función de retorno en un useEffect se utiliza para limpiar o deshacer cualquier efecto secundario que el efecto haya creado. Esto es particularmente útil para evitar posibles problemas de memoria o fugas de recursos al desmontar un componente o cuando se vuelven a aplicar las dependencias.
.

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    // Creamos un temporizador que incrementa los segundos cada segundo
    const intervalId = setInterval(() => {
      setSeconds(prevSeconds => prevSeconds + 1);
    }, 1000);

    // Función de retorno que se ejecuta cuando el componente se desmonta
    return () => {
      clearInterval(intervalId); // Limpiamos el temporizador
    };
  }, []); // La dependencia está vacía, por lo que el efecto se ejecuta solo una vez al montar el componente

  return (
    <div>
      <p>Tiempo transcurrido: {seconds} segundos</p>
    </div>
  );
}

.
En este ejemplo, tenemos un componente Timer que muestra el tiempo transcurrido en segundos. Utilizamos useEffect para iniciar un temporizador que incrementa los segundos cada segundo. Cuando el componente se desmonta, queremos limpiar este temporizador para evitar que continúe ejecutándose. Por lo tanto, utilizamos la función de retorno de useEffect para limpiar el temporizador utilizando clearInterval.
.
El primer renderizado del componente, la función de retorno del useEffect no se ejecuta. La función de retorno del useEffect solo se ejecuta cuando el componente se desmonta, es decir, cuando ya no es necesario mostrar el componente en la interfaz de usuario y se elimina del árbol de componentes renderizados.
.
Así que para aclarar:
.

  • En el primer renderizado, primero se ejecuta el cuerpo del useEffect para establecer los efectos secundarios.
    .
  • La función de retorno del useEffect no se ejecuta durante el primer renderizado. Se ejecutará más adelante, cuando el componente se desmonte.

.
El mecanismo de desmontaje de un componente en React es automático y ocurre cuando:
.

  • Cambio en el estado o las props: Si un componente padre cambia su estado o sus propiedades de tal manera que el componente hijo ya no es necesario para ser renderizado, React desmontará automáticamente el componente hijo del árbol de componentes renderizados.
    .
  • Eliminación explícita: Puedes desmontar un componente explícitamente llamando a ReactDOM.unmountComponentAtNode(container) si conoces el contenedor del componente que deseas desmontar. Esto es menos común y generalmente no se usa a menos que sea realmente necesario.

.
En resumen, useEffect en React es un hook que permite ejecutar efectos secundarios en componentes funcionales. Puede usarse para realizar operaciones como suscripciones a eventos, peticiones de red o manipulaciones del DOM. Además, proporciona una manera de limpiar estos efectos cuando el componente se desmonta o cuando ciertas dependencias cambian.

Tengo que decir que cada dia me gusta mas programar y en gran parte ha sido por Juan
este hoock es muy importante :O

Pienso que adicionar el doble admiración a la condifición if(!!loading) { … } es completamente innecesario y en su lugar hace el código más verboso. Es más simple y entendible if(loading) { …}

Código de la clase:

import React from "react";

function UseState({ name }) {
  const [error, setError] = React.useState(false);
  const [loading, setLoading] = React.useState(false);
  React.useEffect(() => {
    console.log("Empezando el efecto");

    if (!!loading) {
      setTimeout(() => {
        console.log("Haciendo la validación");

        setLoading(false);

        console.log("terminando la validación");
      }, 3000);
    }
    console.log("Terminando el efecto");
  }, [loading]);

  return (
    <div>
      <h2>Eliminar {name}</h2>

      <p>Por favor, escribe el código de seguridad.</p>
      {error && <p>Error: el cédigo es incorrecto</p>}
      {loading && <p>Cargando...</p>}
      <input placeholder="Código de seguridad" />
      <button onClick={() => setLoading(true)}>Comprobar</button>
    </div>
  );
}

export { UseState };

Código de la clase en TypeScript:
Nota: Puedes cambiar el type por interface sin problema

import { FC, useEffect, useState } from "react"

type Props = {
    name: string
}

const UseState:FC<Props> = ({ name }) => {
    const [error, setError] = useState(true);
    const [loading, setLoading] = useState(false);

    useEffect(()=>{
        console.log('Empezando el efecto');
        if(loading){
            console.log(loading);
            setTimeout(()=>{
                console.log("Haciendo la validación");
                setLoading(false);
                console.log("Terminando la validación");
            },3000)
        }
        console.log('Terminando el efecto');
    },[loading]);

    return(
        <div>
            <h2>Eliminar { name }</h2>
            <p>Por favor, escribe el código de seguridad para comprobar que quieres eliminar</p>

            {error && (
                <p>El código es incorrecto</p>
            )}

            {loading && (
                <p>Cargando ...</p>
            )}


            <input type="text" placeholder="Código de seguridad" />
            <button
                onClick={()=>{setLoading(true)}}
            >Comprobar</button>
        </div>
    )
}

export { UseState }

Buen repaso