您可以使用Inertia
的'cancelActiveVisits'方法来取消visit
清理钩中的活动useEffect
。
我是使用React的新手,所以这可能真的很容易实现,但是即使我做了一些研究,也无法自己解决。如果这太蠢了,请原谅我。
我在Laravel(后端)和React(前端)适配器上使用Inertia.js。如果您不知道惯性,则基本上是:
Inertia.js可让您快速构建现代的单页React,Vue和使用经典的服务器端路由和控制器来精简应用程序。
我正在做一个简单的登录页面,该页面具有一种表单,提交后将执行POST请求以加载下一页。似乎工作正常,但在其他页面上,控制台显示以下警告:
警告:无法在已卸载的组件上执行React状态更新。这是空操作,但它表明应用程序中发生内存泄漏。要修复,请取消useEffect中的所有订阅和异步任务清理功能。
登录时(由Inertia创建)
相关代码(为了避免不相关的行,我已经对其进行了简化:]
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;
现在,我知道我必须执行清除功能,因为请求的承诺就是生成此警告的原因。我知道我应该使用useEffect
,但在这种情况下我不知道如何应用。我已经看到了一个值更改的示例,但是如何在这种调用中做到这一点?
提前感谢。
根据要求,此组件的完整代码:
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;
因为这是异步诺言调用,所以您应该使用一个变量相同的引用(useRef)检查已经卸载的组件,以进行对清理内存泄漏的异步响应的下一步处理:
警告:无法在已卸载的组件上执行React状态更新。
在这种情况下,您应该使用两个钩子:useRef
和useEffect
。
例如,使用 useRef是需要钩子的变量。不像本地变量,React确保在每次执行期间都返回相同的引用渲染。useRef
:变量_isMounted
始终指向相同的引用(不是局部变量)>] >>
示例:
:大多数副作用发生在钩子内部。它将替换很多类Component的LifeCycle(ComponentDidMount,componentDidUpate)const login = (props) => { const _isMounted= useRef(true); // Initial value _isMounted = true useEffect(() => { return () => { _isMounted.current = false; // ComponentWillUnmount } }, []); function handleSubmit(e) { e.preventDefault(); setLoading(true); ajaxCall = Inertia.post(window.route('login.attempt'), values) .then(() => { if (_isMounted.current) { // Check always mounted component // continue AJAX response treatment... ; } ) } }
在同一场合,让我向您解释更多有关此处使用的React Hooks的知识。另外,我将比较React Hooks(版本React> 16.8)与类组件的LifeCycle(旧版本)。
useEffect
之后都运行。就像这样:useEffect(fnc, [dependency1, dependency2, ...]); // dependencies array argument is optional
1)如果没有依赖项,则useEffect的默认行为在第一个渲染(例如ComponentDidMount)之后和每个更新渲染(例如ComponentDidUpdate)
useEffect(fnc);
2)将依赖项数组赋予useEffect将更改其生命周期。在这个例子中:useEffect将在第一个渲染之后被调用一次,并且每次计数改变时]
export default function () { const [count, setCount] = useState(0); useEffect(fnc, [count]); }
3)useEffect将在第一个渲染(例如ComponentDidMount)
之后仅运行一次,如果您放置一个空数组作为依赖项。就像这样:useEffect(fnc, []);
4)为防止资源泄漏,必须在挂钩的生命周期结束时全部处理(如ComponentWillUnmount)。例如,对于空的依赖关系数组,将在组件卸载后调用返回的函数。就像这样:
,其。current属性为初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期内保持不变。useEffect(() => { return fnc_cleanUp; // fnc_cleanUp will cancel all subscriptions and asynchronous tasks (ex. : clearInterval) }, []);
useRef
:返回。mutable ref对象
示例:关于上述问题,我们不能在此处使用局部变量,因为它在更新渲染后将丢失。
const login = (props) => { let _isMounted= true; // it isn't good because of a local variable, so the variable will be initiated on every update render useEffect(() => { return () => { _isMounted = false; // not good } }, []); // ... }
因此,结合使用useuseRef和useEffect,我们可以完全清除内存泄漏。
您可以阅读更多有关react挂钩的好链接是:https://medium.com/@sdolidze/the-iceberg-of-react-hooks-af0b588f43fb
您可以使用Inertia
的'cancelActiveVisits'方法来取消visit
清理钩中的活动useEffect
。
因此,通过此调用,活动的visit
将被取消并且状态将不会被更新。
useEffect(() => { return () => { Inertia.cancelActiveVisits(); //To cancel the active visit. } }, []);
如果
Inertia
请求被取消,则它将返回一个空响应,因此您必须添加额外的检查以处理该空响应。也添加添加捕获块以处理任何潜在的错误。function handleSubmit(e) { e.preventDefault(); setLoading(true); Inertia.post(window.route('login.attempt'), values) .then(data => { if(data) { setLoading(false); } }) .catch( error => { console.log(error); }); }
替代方法(解决方法)
您可以使用useRef
保留组件的状态,并据此可以更新state
。
问题:
显示这种冲突是因为handleSubmit
试图更新组件的状态,即使组件已从dom卸下。
解决方案:
设置标志以保持component
的状态,如果component
为mounted
,则flag
值为true
,如果component
为unmounted
,则标志值为假。因此,基于此我们可以更新state
。对于标志状态,我们可以使用useRef
保存参考。
useRef
返回一个可变的ref对象,该对象的.current
属性被初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期内保持不变。在useEffect
中返回一个函数,如果未安装,它将设置组件的状态。然后在清理功能的
useEffect
中,我们可以将标志设置为false.
useEffecr清理功能
useEffect
钩允许使用清除功能。每当效果不再有效时(例如,正在卸载使用该效果的组件时),都会调用此函数以清理所有内容。在我们的例子中,我们可以将标志设置为false。示例:
let _componentStatus.current = useRef(true); useEffect(() => { return () => { _componentStatus.current = false; } }, []);
并且在handleSubmit中,我们可以检查组件是否已安装并基于此更新状态。
function handleSubmit(e) { e.preventDefault(); setLoading(true); Inertia.post(window.route('login.attempt'), values) .then(() => { if (_componentStatus.current) { setLoading(false); } else { _componentStatus = null; } }) }
否则将
_componentStatus
设置为null,以避免任何内存泄漏。
您可以使用Inertia
的'cancelActiveVisits'方法来取消visit
清理钩中的活动useEffect
。