reactjs - 取消 useEffect Hook 中的所有异步/等待任务以防止 react 中内存泄漏的正确方法是什么?

标签 reactjs firebase async-await google-cloud-firestore use-effect

我正在开发一个 React chap 应用程序,该应用程序从 Firebase 数据库中提取数据。在我的“仪表板”组件中,我有一个 useEffect Hook 检查经过身份验证的用户,如果是这样,则从 firebase 中提取数据并设置电子邮件变量和聊天变量的状态。我使用 abortController 进行 useEffect 清理,但是每当我第一次注销并重新登录时,我都会收到内存泄漏警告。

index.js:1375 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

in Dashboard (created by 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 Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.

in Dashboard (created by 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

关于reactjs - 取消 useEffect Hook 中的所有异步/等待任务以防止 react 中内存泄漏的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58562474/

相关文章:

Angular 2 - Firebase Storage -putString() 方法支持元数据吗?

node.js - heroku 服务器和 firebase

javascript - return 不等待异步函数

javascript - 使用 Lerna 将 JavaScript 导入 TypeScript 应用程序

javascript - 在 ReactJS 中将 json 结果推送到数组的正确方法是什么?

ReactJS - 组件未将 this.state.object 正确传递给另一个组件

c# - 使用 .NET 4.5 async、await 将 Azure blob 异步下载到字符串

javascript - React Transition Group 向上滑动元素

android - 如何在 firebase 中获取 key

asynchronous - 用于并行执行的 Flutter 多个异步方法