Soy nuevo usando React, por lo que esto podría ser realmente simple de lograr, pero no puedo resolverlo por mí mismo a pesar de que he investigado un poco. Perdóname si esto es demasiado tonto.
Contexto
Estoy usando Inertia.js con los adaptadores Laravel (backend) y React (front-end). Si no conoce la inercia, básicamente:
Inertia.js le permite crear rápidamente aplicaciones modernas React, Vue y Svelte de una sola página utilizando enrutamiento y controladores clásicos del lado del servidor.
Problema
Estoy haciendo una página de inicio de sesión simple que tiene un formulario que, cuando se envíe, realizará una solicitud POST para cargar la página siguiente. Parece funcionar bien, pero en otras páginas la consola muestra la siguiente advertencia:
Advertencia: no se puede realizar una actualización del estado Reaccionar en un componente desmontado. Esto es un no-op, 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.
en inicio de sesión (creado por Inertia)
El código relacionado (lo he simplificado para evitar líneas irrelevantes):
import React, { useEffect, useState } from 'react'
import Layout from "../../Layouts/Auth";
{/** other imports */}
const login = (props) => {
const { errors } = usePage();
const [values, setValues] = useState({email: '', password: '',});
const [loading, setLoading] = useState(false);
function handleSubmit(e) {
e.preventDefault();
setLoading(true);
Inertia.post(window.route('login.attempt'), values)
.then(() => {
setLoading(false); // Warning : memory leaks during the state update on the unmounted component <--------
})
}
return (
<Layout title="Access to the system">
<div>
<form action={handleSubmit}>
{/*the login form*/}
<button type="submit">Access</button>
</form>
</div>
</Layout>
);
};
export default login;
Ahora, sé que tengo que hacer una función de limpieza porque la promesa de la solicitud es lo que está generando esta advertencia. Sé que debería usarlo, useEffect
pero no sé cómo aplicarlo en este caso. He visto un ejemplo cuando un valor cambia, pero ¿cómo hacerlo en una llamada de este tipo?
Gracias por adelantado.
Actualizar
Según lo solicitado, el código completo de este componente:
import React, { useState } from 'react'
import Layout from "../../Layouts/Auth";
import { usePage } from '@inertiajs/inertia-react'
import { Inertia } from "@inertiajs/inertia";
import LoadingButton from "../../Shared/LoadingButton";
const login = (props) => {
const { errors } = usePage();
const [values, setValues] = useState({email: '', password: '',});
const [loading, setLoading] = useState(false);
function handleChange(e) {
const key = e.target.id;
const value = e.target.value;
setValues(values => ({
...values,
[key]: value,
}))
}
function handleSubmit(e) {
e.preventDefault();
setLoading(true);
Inertia.post(window.route('login.attempt'), values)
.then(() => {
setLoading(false);
})
}
return (
<Layout title="Inicia sesión">
<div className="w-full flex items-center justify-center">
<div className="w-full max-w-5xl flex justify-center items-start z-10 font-sans text-sm">
<div className="w-2/3 text-white mt-6 mr-16">
<div className="h-16 mb-2 flex items-center">
<span className="uppercase font-bold ml-3 text-lg hidden xl:block">
Optima spark
</span>
</div>
<h1 className="text-5xl leading-tight pb-4">
Vuelve inteligente tus operaciones
</h1>
<p className="text-lg">
Recoge data de tus instalaciones de forma automatizada; accede a información histórica y en tiempo real
para que puedas analizar y tomar mejores decisiones para tu negocio.
</p>
<button type="submit" className="bg-yellow-600 w-40 hover:bg-blue-dark text-white font-semibold py-2 px-4 rounded mt-8 shadow-md">
Más información
</button>
</div>
<div className="w-1/3 flex flex-col">
<div className="bg-white text-gray-700 shadow-md rounded rounded-lg px-8 pt-6 pb-8 mb-4 flex flex-col">
<div className="w-full rounded-lg h-16 flex items-center justify-center">
<span className="uppercase font-bold text-lg">Acceder</span>
</div>
<form onSubmit={handleSubmit} className={`relative ${loading ? 'invisible' : 'visible'}`}>
<div className="mb-4">
<label className="block text-gray-700 text-sm font-semibold mb-2" htmlFor="email">
Email
</label>
<input
id="email"
type="text"
className=" appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 outline-none focus:border-1 focus:border-yellow-500"
placeholder="Introduce tu e-mail.."
name="email"
value={values.email}
onChange={handleChange}
/>
{errors.email && <p className="text-red-500 text-xs italic">{ errors.email[0] }</p>}
</div>
<div className="mb-6">
<label className="block text-gray-700 text-sm font-semibold mb-2" htmlFor="password">
Contraseña
</label>
<input
className=" appearance-none border border-red rounded w-full py-2 px-3 text-gray-700 mb-3 outline-none focus:border-1 focus:border-yellow-500"
id="password"
name="password"
type="password"
placeholder="*********"
value={values.password}
onChange={handleChange}
/>
{errors.password && <p className="text-red-500 text-xs italic">{ errors.password[0] }</p>}
</div>
<div className="flex flex-col items-start justify-between">
<LoadingButton loading={loading} label='Iniciar sesión' />
<a className="font-semibold text-sm text-blue hover:text-blue-700 mt-4"
href="#">
<u>Olvidé mi contraseña</u>
</a>
</div>
<div
className={`absolute top-0 left-0 right-0 bottom-0 flex items-center justify-center ${!loading ? 'invisible' : 'visible'}`}
>
<div className="lds-ellipsis">
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
</form>
</div>
<div className="w-full flex justify-center">
<a href="https://optimaee.com">
</a>
</div>
</div>
</div>
</div>
</Layout>
);
};
export default login;
fuente
.then(() => {})
?Respuestas:
Debido a que es la llamada de promesa asíncrona, 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):
Dos ganchos de reacción que debes usar en este caso:
useRef
yuseEffect
.Con
useRef
, por ejemplo, la variable mutable_isMounted
siempre apunta a la misma referencia en la memoria (no una variable local)Ejemplo:
En la misma ocasión, déjame explicarte más información sobre React Hooks que se usa aquí. Además, compararé React Hooks en Functional Component (la versión React> 16.8) con LifeCycle en Class Component.
1) El comportamiento predeterminado de useEffect se ejecuta después del primer render (como ComponentDidMount) y después de cada actualización de actualización (como ComponentDidUpdate) si no tiene dependencias. Es así :
useEffect(fnc);
2) Dar una variedad de dependencias para usar Effect cambiará su ciclo de vida. En este ejemplo: useEffect se llamará una vez después del primer render y cada vez que el recuento cambie
3) useEffect se ejecutará solo una vez después del primer renderizado (como ComponentDidMount) si coloca una matriz vacía para la dependencia. Es así :
useEffect(fnc, []);
4) Para evitar pérdidas de recursos, todo debe desecharse cuando finaliza el ciclo de vida de un gancho (como ComponentWillUnmount) . Por ejemplo, con la matriz vacía de dependencia, la función devuelta se llamará después de que el componente se desmonte. Es así :
Ejemplo: con la pregunta anterior, no podemos usar una variable local aquí porque se perderá y se reiniciará en cada actualización de actualización.
Entonces, con la combinación de useRef y useEffect , podríamos limpiar completamente las pérdidas de memoria.
Los buenos enlaces que podría leer más sobre React Hooks son:
[ES] https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb
[FR] https://blog.soat.fr/2019/11/react-hooks-par-lexemple/
fuente
Puede utilizar el método 'cancelActiveVisits'
Inertia
para cancelar el enlace activovisit
en lauseEffect
limpieza.Entonces, con esta llamada, el activo
visit
se cancelará y el estado no se actualizará.Si la
Inertia
solicitud se cancela, devolverá una respuesta vacía, por lo que debe agregar una verificación adicional para manejar la respuesta vacía. Agregue también agregar bloque de captura para manejar cualquier error potencial.Forma alternativa (solución alternativa)
Puede usar
useRef
para mantener el estado del componente y, en función de esto, puede actualizar elstate
.Problema:
La guerra se muestra porque
handleSubmit
está intentando actualizar el estado del componente a pesar de que el componente se ha desmontado de la dom.Solución:
Establezca una bandera para mantener el estado de la
component
, sicomponent
esmounted
entonces elflag
valor serátrue
y sicomponent
esunmounted
el valor de la bandera será falso. Entonces, en base a esto, podemos actualizar elstate
. Para el estado de la bandera podemos usaruseRef
para mantener una referencia.Y luego, en
useEffect
la función de limpieza, podemos establecer el indicador enfalse.
Función de limpieza useEffecr
Ejemplo:
Y en handleSubmit podemos verificar si el componente está montado o no y actualizar el estado en función de esto.
En caso contrario, establezca el
_componentStatus
valor en nulo para evitar pérdidas de memoria.fuente
ajaxCall
adentrouseEffect
? y ver cuál es el valorundefined
. Lo agregué justo después delreturn () => {