Javascript 清除React钩子中未安装组件上的内存泄漏
我是使用React的新手,所以这可能很容易实现,但即使我做了一些研究,我自己也无法解决。如果这太愚蠢,请原谅我 上下文 我正在使用Laravel(后端)和React(前端)适配器。如果你不知道惯性,它基本上是: Inertia.js允许您快速构建现代的单页React、Vue和 使用经典服务器端路由和控制器的苗条应用程序 问题 我正在做一个简单的登录页面,它有一个表单,提交后将执行POST请求以加载下一个页面。它似乎工作正常,但在其他页面中,控制台显示以下警告: 警告:无法对已卸载的组件执行React状态更新。 这是一个no-op,但它表示应用程序中存在内存泄漏。 若要修复,请取消useEffect中的所有订阅和异步任务 清理功能 登录中(由惯性创建) 相关代码(为了避免不相关的行,我对其进行了简化):Javascript 清除React钩子中未安装组件上的内存泄漏,javascript,reactjs,promise,react-hooks,inertiajs,Javascript,Reactjs,Promise,React Hooks,Inertiajs,我是使用React的新手,所以这可能很容易实现,但即使我做了一些研究,我自己也无法解决。如果这太愚蠢,请原谅我 上下文 我正在使用Laravel(后端)和React(前端)适配器。如果你不知道惯性,它基本上是: Inertia.js允许您快速构建现代的单页React、Vue和 使用经典服务器端路由和控制器的苗条应用程序 问题 我正在做一个简单的登录页面,它有一个表单,提交后将执行POST请求以加载下一个页面。它似乎工作正常,但在其他页面中,控制台显示以下警告: 警告:无法对已卸载的组件执行Rea
您可以使用惯性的“取消活动访问”方法来取消
useffect
清理挂钩中的活动visit
因此,通过此呼叫,活动的访问将被取消,状态将不会更新
useEffect(() => {
return () => {
Inertia.cancelActiveVisits(); //To cancel the active visit.
}
}, []);
如果惯性
请求被取消,那么它将返回一个空响应,因此您必须添加额外的检查来处理空响应。
同时添加catch块以处理任何潜在错误
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
保存组件的状态,并在此基础上更新状态
问题:
let _componentStatus.current = useRef(true);
useEffect(() => {
return () => {
_componentStatus.current = false;
}
}, []);
之所以显示该状态,是因为handleSubmit
正在尝试更新组件的状态,即使组件已从dom中卸载
解决方案:
let _componentStatus.current = useRef(true);
useEffect(() => {
return () => {
_componentStatus.current = false;
}
}, []);
设置一个标志来保持组件的状态
,如果组件
已安装
,则标志
值将为真
,如果组件
未安装
则标志值将为假。因此,基于此,我们可以更新状态
。
对于标志状态,我们可以使用useRef
保存引用
useRef
返回一个可变的ref对象,该对象的.current
属性初始化为传递的参数(initialValue)。返回的对象将在组件的整个生命周期内持续存在。
在useffect
中,返回一个函数,如果组件已卸载,该函数将设置组件的状态
然后在清理函数的useffect
中,我们可以将标志设置为false。
使用有效清除功能
useffect
挂钩允许使用清理功能。当效果不再有效时,例如当使用该效果的组件正在卸载时,将调用此函数来清除所有内容。在我们的例子中,我们可以将标志设置为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;
}
})
}
在else中,将
\u componentStatus
设置为null以避免内存泄漏。因为这是异步承诺调用,所以必须使用可变引用变量(带useRef)检查已卸载的组件,以便下一步处理异步响应(避免内存泄漏):
警告:无法对已卸载的组件执行React状态更新
在本例中应该使用两个React钩子:useRef
和useffect
例如,对于useRef
,可变变量\u isMounted
总是指向内存中的同一引用(而不是局部变量)
如果需要可变变量,useRef是挂钩。不像本地的
变量,React确保在每个过程中返回相同的引用
提供。如果需要,与类组件中的this.myVar相同
例如:
const login=(道具)=>{
const _isMounted=useRef(true);//初始值_isMounted=true
useffect(()=>{
return()=>{//ComponentWillUnmount在类组件中
_isMounted.current=假;
}
}, []);
函数handleSubmit(e){
e、 预防默认值();
设置加载(真);
ajaxCall=Inerty.post(window.route('login.trunt'),值)
.然后(()=>{
如果(_isMounted.current){//检查始终安装的组件
//继续治疗AJAX反应;
}
)
}
}
在同一场合,让我向您解释更多关于这里使用的React钩子的信息。另外,我将比较功能组件(React>16.8)中的React钩子和类组件中的生命周期 useEffect:大多数副作用发生在钩子内部。副作用的示例有:数据获取、设置订阅和手动更改DOM React组件。useEffect替换类组件中的许多生命周期(componentDidMount、componentDidUpate、componentWillUnmount)
useEffect(fnc);
useEffect(fnc,[]);
useEffect(fnc, [dependency1, dependency2, ...]); // dependencies array argument is optional
useEffect(fnc, [count]);
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
}
}, []);
// ...
}
.then(() => {
// Warning : memory leaks during the state update on the unmounted component <--------
setLoading(false);
})