Sou novo no uso do React, por isso pode ser realmente simples de conseguir, mas não consigo descobrir sozinho, mesmo tendo pesquisado. Perdoe-me se isso é muito idiota.
Contexto
Estou usando o Inertia.js com os adaptadores Laravel (back-end) e React (front-end). Se você não conhece a Inércia, basicamente:
O Inertia.js permite criar rapidamente aplicativos modernos React, Vue e Svelte de página única, usando controladores e roteamento clássicos do lado do servidor.
Questão
Estou fazendo uma página de login simples que possui um formulário que, quando enviado, executará uma solicitação POST para carregar a próxima página. Parece funcionar bem, mas em outras páginas o console mostra o seguinte aviso:
Aviso: Não é possível executar uma atualização do estado React em um componente desmontado. Isso não funciona, mas indica um vazamento de memória no seu aplicativo. Para corrigir, cancele todas as assinaturas e tarefas assíncronas em uma função de limpeza useEffect.
no login (criado por Inertia)
O código relacionado (simplifiquei para evitar linhas 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;
Agora, eu sei que tenho que fazer uma função de limpeza, porque a promessa da solicitação é o que está gerando esse aviso. Eu sei que devo usar, useEffect
mas não sei como aplicá-lo neste caso. Já vi exemplo quando um valor muda, mas como fazê-lo em uma chamada desse tipo?
Desde já, obrigado.
Atualizar
Conforme solicitado, o código completo deste 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;
fonte
.then(() => {})
?Respostas:
Como é a chamada de promessa assíncrona, você deve usar uma variável ref mutável (com useRef) para verificar o componente já desmontado para o próximo tratamento da resposta assíncrona (evitando vazamentos de memória):
Dois ganchos de reação que você deve usar neste caso:
useRef
euseEffect
.Com
useRef
, por exemplo, a variável mutável_isMounted
é sempre apontada para a mesma referência na memória (não uma variável local)Exemplo:
Na mesma ocasião, deixe-me explicar mais informações sobre os ganchos de reação usados aqui. Além disso, compararei React Hooks no Componente funcional (a versão React> 16.8) com LifeCycle no Class Component.
1) O comportamento padrão de useEffect é executado após a primeira renderização (como ComponentDidMount) e após cada renderização de atualização (como ComponentDidUpdate) se você não tiver dependências. É assim :
useEffect(fnc);
2) Dar várias dependências para useEffect mudará seu ciclo de vida. Neste exemplo: useEffect será chamado uma vez após a primeira renderização e sempre que a contagem de alterações for alterada
3) useEffect será executado apenas uma vez após a primeira renderização (como ComponentDidMount) se você colocar uma matriz vazia para dependência. É assim :
useEffect(fnc, []);
4) Para evitar vazamentos de recursos, tudo deve ser descartado quando o ciclo de vida de um gancho terminar (como ComponentWillUnmount) . Por exemplo, com a matriz vazia de dependência, a função retornada será chamada após a desmontagem do componente. É assim :
Exemplo: com a pergunta acima, não podemos usar uma variável local aqui porque ela será perdida e reiniciada em cada renderização de atualização.
Portanto, com a combinação de useRef e useEffect , poderíamos limpar completamente os vazamentos de memória.
Os bons links que você pode ler mais sobre os React Hooks são:
[PT] https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb
[FR] https://blog.soat.fr/2019/11/react-hooks-par_Productionsemple/
fonte
Você pode usar o método 'cancelActiveVisits'
Inertia
para cancelar o ativovisit
nouseEffect
gancho de limpeza.Portanto, com esta chamada, o ativo
visit
será cancelado e o estado não será atualizado.se a
Inertia
solicitação for cancelada, ela retornará uma resposta vazia, para que você precise adicionar uma verificação extra para lidar com a resposta vazia. Adicionar add catch block também para lidar com possíveis erros.Maneira alternativa (solução alternativa)
Você pode usar
useRef
para manter o status do componente e, com base nisso, pode atualizar ostate
.Problema:
A guerra está sendo exibida porque
handleSubmit
está tentando atualizar o estado do componente, mesmo que o componente tenha sido desmontado do dom.Solução:
Defina um sinalizador para manter o status de
component
, secomponent
for,mounted
então oflag
valor serátrue
e secomponent
forunmounted
o valor do sinalizador será falso. Então, com base nisso, podemos atualizar ostate
. Para o status da bandeira, podemos usaruseRef
para manter uma referência.E então, na
useEffect
função de limpeza, podemos definir o sinalizador parafalse.
função de limpeza useEffecr
Exemplo:
E no handleSubmit, podemos verificar se o componente está montado ou não e atualizar o estado com base nisso.
Caso contrário, defina
_componentStatus
como null para evitar vazamentos de memória.fonte
ajaxCall
dentrouseEffect
. e veja qual é o valorundefined
. Eu adicionei logo após oreturn () => {