reactjs - 在 useEffect 挂钩中取消所有异步/等待任务以防止反应中的内存泄漏的正确方法是什么?
问题描述
我正在开发一个从 firebase 数据库中提取数据的 react chap 应用程序。在我的“仪表板”组件中,我有一个 useEffect 挂钩来检查经过身份验证的用户,如果是,则从 firebase 中提取数据并设置电子邮件变量和聊天变量的状态。我使用 abortController 进行 useEffect 清理,但是每当我第一次注销并重新登录时,都会收到内存泄漏警告。
index.js:1375 警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。
在仪表板中(由 Context.Consumer 创建)
最初我没有abortController,我只是在清理时返回了一个控制台日志。做了更多的研究并发现了 abortController 但是这些示例使用了 fetch 和 signal 并且我找不到任何与 async/await 一起使用的资源。我愿意更改数据的检索方式(无论是使用 fetch、async/await 还是任何其他解决方案)我只是无法让它与其他方法一起使用。
const [email, setEmail] = useState(null);
const [chats, setChats] = useState([]);
const signOut = () => {
firebase.auth().signOut();
};
useEffect(() => {
const abortController = new AbortController();
firebase.auth().onAuthStateChanged(async _user => {
if (!_user) {
history.push('/login');
} else {
await firebase
.firestore()
.collection('chats')
.where('users', 'array-contains', _user.email)
.onSnapshot(async res => {
const chatsMap = res.docs.map(_doc => _doc.data());
console.log('res:', res.docs);
await setEmail(_user.email);
await setChats(chatsMap);
});
}
});
return () => {
abortController.abort();
console.log('aborting...');
};
}, [history, setEmail, setChats]);
预期结果是在 useEffect 清理函数中正确清理/取消所有异步任务。一个用户注销后,相同或不同的用户重新登录后,我在控制台中收到以下警告
index.js:1375 警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。
在仪表板中(由 Context.Consumer 创建)
解决方案
在firebase的情况下,您不是在处理async/await
而是在处理流。您应该在清理功能中取消订阅 firebase 流:
const [email, setEmail] = useState(null);
const [chats, setChats] = useState([]);
const signOut = () => {
firebase.auth().signOut();
};
useEffect(() => {
let unsubscribeSnapshot;
const unsubscribeAuth = firebase.auth().onAuthStateChanged(_user => {
// you're not dealing with promises but streams so async/await is not needed here
if (!_user) {
history.push('/login');
} else {
unsubscribeSnapshot = firebase
.firestore()
.collection('chats')
.where('users', 'array-contains', _user.email)
.onSnapshot(res => {
const chatsMap = res.docs.map(_doc => _doc.data());
console.log('res:', res.docs);
setEmail(_user.email);
setChats(chatsMap);
});
}
});
return () => {
unsubscribeAuth();
unsubscribeSnapshot && unsubscribeSnapshot();
};
}, [history]); // setters are stable between renders so you don't have to put them here
推荐阅读
- javascript - React-redux 代码不会在 redux dispatch 和 reducer 成功时重新渲染 react 组件
- regex - 为什么这个正则表达式在 grep 中有效,但在 sed 中无效?
- laravel - Laravel Sanctum /登录未找到
- ruby-on-rails - Rails - 在单个迁移文件中创建表和插入数据
- batch-file - 如何检查用户在批处理文件中输入了多少输入?
- python - 如何在字符串(python)中与=一起删除单词?
- c++ - Visual Studio:从可执行文件中获取所有证书颁发者详细信息
- spring-kafka - kafka 分区何时/为什么可以“自行”取消暂停?
- javascript - 设计表单构建器的数据库和状态突变和请求,以与 graphQL、动物区系数据库、nextJS 和 Apollo 做出反应
- reactjs - 如何定义一个可以通过 Typescript 中的变量内容访问其项目的对象?