react hooks useEffect () limpieza solo para componentWillUnmount?

83

Permítanme explicar el resultado de este código para preguntar mi problema fácilmente.

const ForExample = () => {
    const [name, setName] = useState('');
    const [username, setUsername] = useState('');

    useEffect(() => {
        console.log('effect');
        console.log({
            name,
            username
        });

        return () => {
            console.log('cleaned up');
            console.log({
                name,
                username
            });
        };
    }, [username]);

    const handleName = e => {
        const { value } = e.target;

        setName(value);
    };

    const handleUsername = e => {
        const { value } = e.target;

        setUsername(value);
    };

    return (
        <div>
            <div>
                <input value={name} onChange={handleName} />
                <input value={username} onChange={handleUsername} />
            </div>
            <div>
                <div>
                    <span>{name}</span>
                </div>
                <div>
                    <span>{username}</span>
                </div>
            </div>
        </div>
    );
};

Cuando se ForExample componentmonte, se registrará el 'efecto'. Esto está relacionado con el componentDidMount().

Y cada vez que cambio la entrada de nombre, se registrarán tanto el 'efecto' como la 'limpieza'. Viceversa, no se registrará ningún mensaje cada vez que cambie la entrada del nombre de usuario desde que agregué [username]al segundo parámetro de useEffect(). Esto está relacionado con lacomponentDidUpdate()

Por último, cuando se ForExample componentdesmonte, se registrará 'limpiado'. Esto está relacionado con el componentWillUnmount().

Todos sabemos eso.

En resumen, se invoca 'limpiado' cada vez que el componente se vuelve a renderizar (incluye desmontar)

Si quiero que este componente se registre 'limpiado' solo en el momento en que se desmonte, solo tengo que cambiar el segundo parámetro de useEffect()a [].

Pero si cambio [username]a [], ForExample componentya no implementa la componentDidUpdate()entrada de nombre.

Lo que quiero hacer es que, para que el componente admita componentDidUpdate()solo para la entrada de nombre y componentWillUnmount(). (registro 'limpiado' solo en el momento en que se desmonta el componente)

koo
fuente
4
Podrías tener 2 efectos separados. Uno al que se le da una matriz con usernamecomo segundo argumento, y uno al que se le da una matriz vacía como segundo argumento.
Tholle
@Tholle ¿Quiere decir que tengo que hacer 2 métodos useEffect () separados?
koo
1
Sí, esa es una forma de hacerlo.
Tholle
1
@Tholle Pensé que sería anulado por el último método useEffect (). Lo intentaré. Gracias
koo
2
¡Excelente! De nada. Depende de lo que deba hacer la limpieza. 2 efectos separados no es una mala solución.
Tholle

Respuestas:

81

Dado que la limpieza no depende de username, puede poner la limpieza en un lugar separado useEffectque reciba una matriz vacía como segundo argumento.

Ejemplo

const { useState, useEffect } = React;

const ForExample = () => {
  const [name, setName] = useState("");
  const [username, setUsername] = useState("");

  useEffect(
    () => {
      console.log("effect");
    },
    [username]
  );

  useEffect(() => {
    return () => {
      console.log("cleaned up");
    };
  }, []);

  const handleName = e => {
    const { value } = e.target;

    setName(value);
  };

  const handleUsername = e => {
    const { value } = e.target;

    setUsername(value);
  };

  return (
    <div>
      <div>
        <input value={name} onChange={handleName} />
        <input value={username} onChange={handleUsername} />
      </div>
      <div>
        <div>
          <span>{name}</span>
        </div>
        <div>
          <span>{username}</span>
        </div>
      </div>
    </div>
  );
};

function App() {
  const [shouldRender, setShouldRender] = useState(true);

  useEffect(() => {
    setTimeout(() => {
      setShouldRender(false);
    }, 5000);
  }, []);

  return shouldRender ? <ForExample /> : null;
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>

Tholle
fuente
ejemplo agradable y limpio. Aunque me pregunto. ¿Puedo activar un efecto de uso de alguna manera en la navegación modificada, o tendré que moverlo hacia arriba en el árbol de componentes? Porque cuando simplemente pego su useEffect "limpiado", no veo este disparador.
Fabian Bosler
113

puede utilizar más de un efecto de uso

por ejemplo, si mi variable es data1, puedo usar todo esto en mi componente

useEffect( () => console.log("mount"), [] );
useEffect( () => console.log("will update data1"), [ data1 ] );
useEffect( () => console.log("will update any") );
useEffect( () => () => console.log("will update data1 or unmount"), [ data1 ] );
useEffect( () => () => console.log("unmount"), [] );
Mehran Motiee
fuente
4
¿Cuál es la diferencia entre el primer y el último useEffects, el primer useEffect se invocará en willmount o didmount , el último useEffect se devolvió la función de devolución de llamada con una matriz vacía por qué? ¿Podría elaborar cada caso de uso useEffect cuándo y cómo podemos usar?
siluveru kiran kumar
3
@siluverukirankumar El valor de retorno (función) de una devolución de llamada es lo que se llama al destruir (evento de desmontaje). Es por eso que el último ejemplo es un HOC, que devuelve la función inmediatamente. El segundo parámetro es donde React buscaría cambios para volver a ejecutar este gancho. Cuando es una matriz vacía, se ejecutará solo una vez.
Georgy
3
Gracias @Georgy consiguió el último useEffect está regresando de devolución de llamada no se ve con claridad
siluveru Kiran Kumar
2
Entonces, si hace un gancho useEffect, y devuelve una función ... entonces el código antes de la función devuelta se ejecuta como componentDidMount ... y el código en la función devuelta se llama para componentWillUnmount? Es un poco confuso, así que asegúrate de entender bien. useEffect (() => {// código para ejecutar en el montaje ... return () => {// código para ejecutar el desmontaje}}) ¿Es eso correcto?
Maiya
Sugiero leer overreacted.io/a-complete-guide-to-useeffect Pensar en ganchos en los ciclos de vida no es realmente tan dulce
moto
2
function LegoComponent() {

  const [lego, setLegos] = React.useState([])

  React.useEffect(() => {
    let isSubscribed = true
    fetchLegos().then( legos=> {
      if (isSubscribed) {
        setLegos(legos)
      }
    })
    return () => isSubscribed = false
  }, []);

  return (
    <ul>
    {legos.map(lego=> <li>{lego}</li>)}
    </ul>
  )
}

En el código anterior, la función fetchLegos devuelve una promesa. Podemos "cancelar" la promesa al tener un condicional en el alcance de useEffect, evitando que la aplicación establezca el estado después de que el componente se haya desmontado.

Advertencia: No se puede realizar una actualización de estado de React en un componente desmontado. Esta es una operación no operativa, pero indica una pérdida de memoria en su aplicación. Para solucionarlo, cancele todas las suscripciones y tareas asincrónicas en una función de limpieza useEffect.

Daniel Yap
fuente
0

useEffect se aíslan dentro de su propio ámbito y se representan en consecuencia. Imagen de https://reactjs.org/docs/hooks-custom.html

ingrese la descripción de la imagen aquí

Peter Anthony Duot
fuente
Un enlace a una solución es bienvenido, pero asegúrese de que su respuesta sea útil sin él: agregue contexto alrededor del enlace para que sus compañeros usuarios tengan una idea de qué es y por qué está allí, luego cite la parte más relevante de la página. está enlazando a en caso de que la página de destino no esté disponible. Las respuestas que son poco más que un enlace pueden eliminarse .
Богдан Опир
0

en lugar de crear demasiadas funciones y métodos complicados, lo que hago es crear un detector de eventos y automáticamente hago el montaje y desmontaje sin tener que preocuparme por hacerlo manualmente. Aquí hay un ejemplo.

//componentDidMount
useEffect( () => {

    window.addEventListener("load",  pageLoad);

    //component will unmount
    return () => {
       
        window.removeEventListener("load", pageLoad);
    }

 });

ahora que esta parte está hecha, simplemente ejecuto todo lo que quiera desde la función pageLoad de esta manera.

const pageLoad = () =>{
console.log(I was mounted and unmounted automatically :D)}
Jerryurenaa
fuente