reactjs - 在 useEffect 中反应内存泄漏警告
问题描述
以下代码给了我泄漏内存警告:
警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。
const prepareMetaData = async () => {
const languagesJson = localStorage.getItem('feedsLanguages');
const platformModelsJson = localStorage.getItem('feedsPlatformModels');
let languagesArr;
let platformsArr;
if (!ValidationUtils.isExists(languagesJson) || !ValidationUtils.isExists(platformModelsJson)) {
const res = await fetch(buildApiUrl('feeds/meta-data', true, false), buildFetchRequest('GET'));
await handleFailedFetchRequests(res);
const metaData = await res.json();
languagesArr = metaData.languages;
platformsArr = metaData.platformModels;
localStorage.setItem('feedsLanguages', JSON.stringify(metaData.languages));
localStorage.setItem('feedsPlatformModels', JSON.stringify(metaData.platformModels));
} else {
languagesArr = JSON.parse(languagesJson);
platformsArr = JSON.parse(platformModelsJson);
}
const langObj = TextUtils.setLanguages(languagesArr);
setLanguagesDict(langObj);
setPlatformModels(platformsArr);
};
const prepareData = async () => {
prepareMetaData();
};
useEffect(() => {
prepareData();
}, []);
所以我尝试了以下方法:
let isCanceled = false;
const prepareMetaData = async () => {
const languagesJson = localStorage.getItem('feedsLanguages');
const platformModelsJson = localStorage.getItem('feedsPlatformModels');
let languagesArr;
let platformsArr;
if (!ValidationUtils.isExists(languagesJson) || !ValidationUtils.isExists(platformModelsJson)) {
const res = await fetch(buildApiUrl('feeds/meta-data', true, false), buildFetchRequest('GET'));
await handleFailedFetchRequests(res);
const metaData = await res.json();
languagesArr = metaData.languages;
platformsArr = metaData.platformModels;
localStorage.setItem('feedsLanguages', JSON.stringify(metaData.languages));
localStorage.setItem('feedsPlatformModels', JSON.stringify(metaData.platformModels));
} else {
languagesArr = JSON.parse(languagesJson);
platformsArr = JSON.parse(platformModelsJson);
}
const langObj = TextUtils.setLanguages(languagesArr);
if (!isCanceled) {
setLanguagesDict(langObj);
setPlatformModels(platformsArr);
}
};
const prepareData = async () => {
prepareMetaData();
};
useEffect(() => {
prepareData();
return () => {
isCanceled = true;
}
}, []);
但它仍然无法正常工作。任何可行的解决方案?
谢谢。
解决方案
在异步函数中,您无法确定这两个setState
调用是否在组件卸载之前运行。
setLanguagesDict(langObj);
// other stuff can happen here
setPlatformModels(platformsArr);
要解决此问题,使用您的解决方案,您需要在每次调用之前分别检查 isCancel 。
if (!isCanceled) {
setLanguagesDict(langObj);
}
if (!isCanceled) {
setPlatformModels(platformsArr);
}
要确认/复制问题(并证明解决方案有效),您可以检查此 REPL.it(特别是 App.js)文件。
需要明确的是 - 这是一个非常混乱的问题解决方案。我会考虑通过使用 refs 或 reducer 模式来避免重新渲染(显然取决于使用上下文)。
简而言之,我采用了 create react app 模板,并创建了一个非常愚蠢的 App.js,它在第一个 setState 后卸载,在效果运行后返回。在控制台打开的情况下运行它,您将看到消息出现。取消注释中间测试以查看它是否已修复。
import React from 'react';
import logo from './logo.svg';
import './App.css';
let internalCounter = 0;
let toggledIsActive = false;
const TogglingComponent = ({
initialCounter,
setCounter,
}) => {
const [counterToShow, setInternalCounter] = React.useState(initialCounter);
React.useEffect(() => {
toggledIsActive = true;
const waitAndUpdate = async () => {
await new Promise(resolve => {
setTimeout(() => resolve(), 1500);
});
internalCounter++;
if (toggledIsActive) {
setCounter(internalCounter);
// Uncomment to fix state update
// }
// if (toggledIsActive) {
setInternalCounter(internalCounter);
}
}
const interval = setInterval(waitAndUpdate, 1500);
return () => {
toggledIsActive = false;
clearInterval(interval);
};
});
return <a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React {counterToShow}
</a>;
}
function App() {
const [counter, setCounter] = React.useState(0);
const [lastCounter, setLastCounter] = React.useState(counter);
React.useEffect(() => {
setLastCounter(counter);
}, [counter])
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code>!
</p>
{counter === lastCounter ? <TogglingComponent
initialCounter={lastCounter}
setCounter={setCounter}
/> : null}
</header>
</div>
);
}
推荐阅读
- javascript - 有没有办法使用 chrome 开发者工具在我的 chrome 扩展中提供的屏幕截图 API?
- windows - 无法在主分区上设置恢复分区 ID
- python - 如何确保每个工作人员只使用一个 CPU?
- assembly - NASM - 创建文件或目录时如何设置权限
- php - 为什么在 Windows 10 中尝试使用 PHP CLI 删除(空)目录时出现“rmdir ... Permission denied:”错误?
- python - Pandas 对两个数字之间的列进行操作
- java - 当我添加对象时,用于在 Java 中存储对象的 ArrayList 不起作用
- linux - Deployer SSH bash 未加载配置文件
- css - 带有引导程序的透明导航栏无法按预期工作
- javascript - 为什么在将条件放入其中时for循环会在浏览器中崩溃?