首页 > 解决方案 > 使用 Firebase 在 useEffect 上停止无限循环

问题描述

我第一次用钩子测试firebase,遇到了众所周知的无限循环问题。

我知道还有很多其他问题可能与这个问题很接近,但在这种情况下我仍然无法解决这个问题。

这里是 App.js 上的代码:

import React, { useState, useEffect } from 'react';
import { auth, createUserProfileDocument } from './firebase/firebase.utils';

function App() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    auth.onAuthStateChanged(async userAuth => {
      if (userAuth) {
        const userRef = await createUserProfileDocument(userAuth);

        userRef.onSnapshot(snapshot => {
          setUser({
            user: {
              id: snapshot.id,
              ...snapshot.data()
            }
          })
        });

        console.log(user);

      } else {
        setUser(null);
      }
    });
  }, [user]);

  return (
    <div></div>
  );
}

export default App;

这里是 firebase.utils.js 的 createUserProfileDocument 函数:

export const createUserProfileDocument = async (userAuth, additionalData) => {
    if (!userAuth) return;

    const userRef = firestore.doc(`users/${userAuth.uid}`);

    const snapshot = await userRef.get();

    if (!snapshot.exists) {
        const { displayName, email } = userAuth;
        const createdAt = new Date();

        try {
            await userRef.set({
                displayName,
                email,
                createdAt,
                ...additionalData
            })
        } catch (err) {
            console.log('Error creating user', err.message);
        }
    }

    return userRef;
};

在这种情况下,我将如何检查 onAuthStateChanged 是否实际发生了变化?我得到的印象是 auth.onAuthStateChanged 函数每次都会触发,生成那个无限循环..

这里我检查的资源可能会有所帮助:

先感谢您。

标签: reactjsfirebasereact-hooks

解决方案


该方法onAuthStateChanged通过为用户的登录状态添加观察者来设置订阅。只需要在组件挂载时订阅一次,组件卸载时调用unsubscribe,防止观察者运行导致内存泄漏。

要回答为什么在您的情况下导致无限循环,您将user状态作为依赖项,这将导致每次user更新状态值时重新初始化观察者,并且观察者将返回一个全新的userAuth对象并setUser再次被调用,这将更新用户状态,这将重新初始化观察者并一遍又一遍地重复相同的循环。

解决方案,

通过将空数组作为依赖项传递给组件,仅在组件挂载时设置一次订阅useEffect

function App() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    // auth.onAuthStateChanged will return a firebase.Unsubrcibe function
    // which you can call to terminate the subscription
    const unsubscribe = auth.onAuthStateChanged(async userAuth => {
      if (userAuth) {
        const userRef = await createUserProfileDocument(userAuth);

        userRef.onSnapshot(snapshot => {
          setUser({
            user: {
              id: snapshot.id,
              ...snapshot.data()
            }
          })
        });

      } else {
        setUser(null);
      }
    });

   // return a clean up function that will call unsubscribe to -
   // terminate the subscription when component unmounts
   return () => { unsubscribe() }
  }, []); // important set an empty array as dependency

  return (
    <div></div>
  );
}

export default App;

编辑-不要尝试将user变量记录在同一个useEffect钩子中,因为它需要一个空的依赖数组,而是使用另一个useEffect钩子来记录user传递useras 依赖项的值。例子

useEffect(() => {
 console.log(user)
}, [user])

推荐阅读