首页 > 解决方案 > onAuthStateChanged 被调用得太早

问题描述

我很想用一些额外的数据(例如:subscribeToEmail 字段等)创建一个新用户。根据我在网上阅读的内容,执行此操作的方法是对用户进行身份验证(例如:通过使用createUserWithEmailAndPassword),然后使用uid我从中获得的在用户集合中创建一个新文档。这就是我在下面尝试做的事情:

const handleSignup = async (formData) => {
  const {email, password, ...otherUserData} = formData;
  const {user} = await auth.createUserWithEmailAndPassword(email,  password);
  console.log("generating user doc at signup...");
  await generateUserDocument(user, otherUserData); // creates user doc (called 2nd)
}

将使用从函数调用中获得的 uid 在集合中generateUserDocument创建一个用户文档(如果它不存在) 。我还设置了一个身份验证状态更改事件处理程序:usersusercreateUserWithEmailAndPassword

auth.onAuthStateChanged(async (userAuth) => { // get the current user 
  if (userAuth) {
    const user = await generateUserDocument(userAuth); // user (should) already exists, so generateUserDocument will fetch the user rather than creating a new user doc, but this doesn't happen, as it is called 1st and not 2nd
    dispatch(login(user)); // login logic
  } else {
    dispatch(logout());
  }
});

createUserWithEmailAndPassword这里的问题是,当我调用onAuthStateChanged回调触发器时,它会generateUserDocument 我实际使用generateUserDocument(user, otherUserData);handleSignup 方法内部创建用户文档之前调用。换句话说, fetch user 方法: generateUserDocumentinside of.onAuthStateChange()是在实际创建用户之前调用的,这是由方法generateUserDocument内部完成的handleSignup。因此,我在 authStateChange 中获取的用户数据不包括我所追求的详细信息。

有没有办法解决这个问题,以便在 onAuthStateChange 事件处理程序执行之前(而不是之后)auth.createuserWithEmailAndPassword()调用我的函数调用?我已经考虑过使用类似的东西,但我认为这可能有点矫枉过正,因为用户数据表不应该真的需要被连续收听,因为它很少会改变。最好有一个生命周期方法在我可以用来填充我的用户集合之前被调用,但我还没有找到太多关于它的东西。.onSnapshot()onAuthStateChanged

作为参考,我一直在关注这篇关于将其他用户数据与 auth-user 记录相关联的文章。

标签: javascriptfirebasegoogle-cloud-firestorefirebase-authentication

解决方案


有没有办法解决这个问题,以便 auth.createuserWithEmailAndPassword()onAuthStateChange事件处理程序执行之前(而不是之后)调用我的函数调用?

不,没有开箱即用的方法,因为

  1. 成功创建用户帐户后,createuserWithEmailAndPassword()该用户也将登录到您的应用程序(请参阅文档),并且
  2. onAuthStateChange()观察者在登录或注销时触发。

因此,您确实需要等待创建用户 Firestore 文档,然后再继续。在我看来,最好的方法是你提到的那个,即为user文档设置一个监听器。

您可以这样做,在您第一次从用户文档中获取数据后立即取消侦听器,如下所示。这样用户文档就不会被连续收听

  auth.onAuthStateChanged((userAuth) => {    // No more need to be async
    // get the current user
    if (userAuth) {
      const userDocRef = firestore.collection('users').doc(userAuth.uid);
      const listener = userDocRef.onSnapshot((doc) => {
        if (doc.exists) {
          console.log(doc.data());
          // Do any other action you need with the user's doc data

          listener();   // Calling the unsubscribe function that cancels the listener

          dispatch(login(user));
        }
      });
    } else {
      dispatch(logout());
    }
  });

另一种方法可能是使用Callable Cloud Function,它在后端在 Auth 服务中创建用户并在 Firestore 中创建文档。您的handleSignup功能如下:

const handleSignup = async (formData) => {
  const createUser = firebase.functions().httpsCallable('createUser');
  await createUser({formData});
  // The user is created in the Auth service
  // and the user doc is created in Firestore

  // We then need to signin the user, since the call to the Cloud Function did not do it!
  const {email, password, ...otherUserData} = formData;
  await auth.signInWithEmailAndPassword(email, password);
  // The onAuthStateChanged listener is triggered and the Firestore doc does exist
}

推荐阅读