Saltar al contenido

Limpieza de fugas de memoria en un componente desmontado en React Hooks

No olvides que en las ciencias informáticas un problema casi siempre tiene diferentes resoluciones, pero enseñaremos la mejor y más eficiente.

Solución:

Porque es la llamada de promesa asíncrona, por lo que debe usar una variable de referencia mutable (con useRef) para verificar el componente ya desmontado para el próximo tratamiento de la respuesta asíncrona (evitando pérdidas de memoria):

Advertencia: no se puede realizar una actualización de estado de React en un componente desmontado.

Dos React Hooks que deberías usar en este caso: useRef y useEffect.

Con useRefpor ejemplo, la variable mutable _isMounted siempre apunta a la misma referencia en la memoria (no es una variable local)

useRef es el gancho de acceso si se necesita una variable mutable. A diferencia de las variables locales, React se asegura de que se devuelva la misma referencia durante cada renderizado. Si quieres, es lo mismo con this.myVar en el componente de clase

Ejemplo :

const login = (props) => {
  const _isMounted = useRef(true); // Initial value _isMounted = true

  useEffect(() => 
    return () =>  // ComponentWillUnmount in Class Component
        _isMounted.current = false;
    
  , []);

  function handleSubmit(e) 
    e.preventDefault();
    setLoading(true);
    ajaxCall = Inertia.post(window.route('login.attempt'), values)
        .then(() => 
            if (_isMounted.current)  // Check always mounted component
               // continue treatment of AJAX response... ;
            
         )
  


En la misma ocasión, déjame explicarte más información sobre los React Hooks que se usan aquí. Además, compararé React Hooks en Functional Component (React >16.8) con LifeCycle en Class Component.

efecto de uso : La mayoría de los efectos secundarios ocurren dentro del anzuelo. Ejemplos de efectos secundarios son: obtención de datos, configuración de una suscripción y cambio manual de los componentes de DOM React. El useEffect reemplaza una gran cantidad de ciclos de vida en el componente de clase (componentDidMount, componenteDidUpate, componenteWillUnmount)

 useEffect(fnc, [dependency1, dependency2, ...]); // dependencies array argument is optional
  1. El comportamiento predeterminado de useEffect se ejecuta después del primer procesamiento (como ComponentDidMount) y después de cada renderizado de actualización (como ComponentDidUpdate) si no tienes dependencias. Es así : useEffect(fnc);

  2. Donación array de dependencias a useEffect cambiará su ciclo de vida. En este ejemplo: useEffect se llamará una vez después del primer procesamiento y cada vez que cambie el recuento

    exportar función predeterminada () const [count, setCount] = usarEstado(0);

    useEffect(fnc, [count]);
    

  3. useEffect se ejecutará solo una vez después del primer procesamiento (como ComponentDidMount) si pones un vacio array por dependencia. Es así : useEffect(fnc, []);

  4. Para evitar fugas de recursos, todo debe desecharse cuando finaliza el ciclo de vida de un gancho. (como ComponentWillUnmount). Por ejemplo, con el vacío array de dependencia, la función devuelta se llamará después de que se desmonte el componente. Es así :

    useEffect(() => return fnc_cleanUp; // fnc_cleanUp cancelará todas las suscripciones y tareas asincrónicas (ej.: clearInterval) , []);

useRef : devoluciones un objeto ref mutable cuyo .Actual La propiedad se inicializa en el argumento pasado (valor inicial). El objeto devuelto persistirá durante toda la vida útil del componente.

Ejemplo: con la pregunta anterior, no podemos usar una variable local aquí porque se perderá y se reiniciará en cada renderizado de actualización.

const login = (props) => 
  let _isMounted= true; // it isn't good because of a local variable, so the variable will be lost and re-defined on every update render

  useEffect(() => 
    return () => 
        _isMounted = false;  // not good
    
  , []);

  // ...

Entonces, con la combinación de useRef y efecto de usopodríamos limpiar por completo las fugas de memoria.


Los buenos enlaces en los que puede leer más sobre los React Hooks son:

[EN] https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb

[FR] https://blog.soat.fr/2019/11/react-hooks-par-lexemple/

Antes de mutar el estado, primero debe verificar si el componente aún está montado.

Como dijo @SanjiMika anteriormente, cuando tiene una acción asíncrona que causa este error, significa que está tratando de mutar el estado del componente después de que se desmontó.

react-use proporciona ganchos para eso, tienes 2 opciones:

opción #1: useMountedState

// check if isMounted before changing any state
const isMounted = useMountedState();

useEffect(() => 
  const asyncAction = executeAsyncAction();

  asyncAction.then(result => 
    if (isMounted) 
      // It's safe to mutate state here
    
  );
, []);

opción #2: useUnmountPromise

/* `resolveWhileMounted` wraps your promise, and returns a promise that will resolve 
 * only while the component is still mounted */
const resolveWhileMounted = useUnmountPromise();

useEffect(async () => 
  const asyncAction = executeAsyncAction();

  resolveWhileMounted(asyncAction).then(result => 
    // It's safe to mutate state here
  );
, []);

Eres capaz de añadir valor a nuestra información aportando tu veteranía en los comentarios.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *