Firebase onauthstatechanged 在页面加载时触发不一致

Posted

技术标签:

【中文标题】Firebase onauthstatechanged 在页面加载时触发不一致【英文标题】:Firebase onauthstatechanged firing inconsistently on page load 【发布时间】:2022-01-04 01:27:19 【问题描述】:

我目前正在使用 react 17、react-router-dom v6 和 firebase v9 构建一个网络应用程序。

具体来说,在我的 iPhone SE(同时使用 safari 和 chrome)上加载页面时,我有时会卡在加载状态,因为应用无法确定用户是否登录。

在进一步调试后,我意识到如果我以前登录过,加载页面时并不总是触发 onAuthStateChanged。我可以通过手动刷新迫使 onAuthStateChanged 再次触发的浏览器来规避这种行为,这标志着我回到原点。

据我了解,onAuthStateChanged 应在加载页面时至少触发一次,以确定用户是否通过身份验证。

这种行为特别常见,我在登录时手动在 URL 栏中输入 /login 路径..

我已将路由包装在 AuthProvider 中,如 react-router auth example 所示。我还使用警卫来防止用户在通过身份验证时导航到“/login”,而在未通过身份验证时导航到“/”。

任何帮助或建议将不胜感激。

AuthProvider.js

export function AuthProvider(children) 
  const [currentUser, setCurrentUser] = useState(null);
  const [isInitialized, setisInitialized] = useState(false);

  
  useEffect(() => 
    const unsubscribe = onAuthStateChanged(
      auth,
      (user) => 
        
        setCurrentUser(user);
        setIsInitialized(true);
      ,
    );

    return unsubscribe;
  , []);

  const value = 
    currentUser,
  ;

  if (isInitialized === false) 
    return <LoadingScreen />;
  
  return <AuthContext.Provider value=value>children</AuthContext.Provider>;

App.js

function App() 
  return (
    <div style=backgroundColor: 'black'>
      <AuthProvider>
        <Routes>
          <Route
            path="/login"
            element=
              <RequireLoggedOut>
                <Login />
              </RequireLoggedOut>
            
          />

          <Route
            path="/"
            element=
              <RequireAuth>
                <Dashboard />
              </RequireAuth>
            
          />
          
          <Route
            path="/phone"
            element=
              <RequireLoggedOut>
                <LoginPhone />
              </RequireLoggedOut>
            
          />

          <Route
            path="/profile"
            element=
              <RequireAuth>
                <Profile />
              </RequireAuth>
            
          />
        </Routes>
      </AuthProvider>
    </div>
  );

RequireAuth.js

export default function RequireAuth(children) 
  const currentUser = useAuth();
  let location = useLocation();

  if (!currentUser) 
    return <Navigate to="/login" state=from: location />;
  
  return children;

RequireLoggedOut.js

export default function RequireLoggedOut(children) 
  const currentUser = useAuth();
  let location = useLocation();

  if (currentUser) 
    return <Navigate to="/" state=from: location />;
  
  return children;

更新 1 2021-11-26

我按照 Noah Gwynn 的建议添加了路径名作为依赖项,以及一些日志输出。

useEffect(() => 
    console.log('Mounting AuthContext');
    return () => 
      console.log('Cleaning authcontext');
    ;
  , []);

useEffect(() => 
    console.log('AuthContext path: ' + pathname);
    console.log('Attaching onAuthStateChanged');
    const unsubscribe = onAuthStateChanged(auth, (user) => 
      console.log('auth state changed');
      setCurrentUser(user);
      setisInitialized(true);
    );

    return () => 
      console.log('Detaching onAuth..');
      unsubscribe();
    ;
  , [pathname]);

在尝试通过在 URL 栏中手动输入来加载 /login 路径时,大约 20% 的时间会得到此输出。

LOG: Mounting AuthContext
LOG: AuthContext path: /login
LOG: Attaching onAuthStateChanged

这个输出,在其余时间正确地触发观察者。

LOG: Mounting AuthContext
LOG: AuthContext path: /login
LOG: Attaching onAuthStateChanged
LOG: auth state changed
LOG: Detaching onAuth..
LOG: AuthContext path: /
LOG: Attaching onAuthStateChanged
LOG: auth state changed

更新 2 2021-11-26

此外,如果我将 iPhone 连接到 MacOS 上的 Safari 调试工具,问题就会消失,并且每次都会正确调用 onAuthStateChanged。一旦我禁用调试,行为就会返回,并且除非我刷新页面,否则并不总是调用 onAuthStateChanged..

【问题讨论】:

我知道最近 Safari 出现了一些问题,例如 this one。您使用的是哪个版本的 Firebase Auth SDK? 我目前正在运行 Firebase 9.5.0。但是,我在 Chrome 上也遇到了这个问题。 【参考方案1】:

如果您想确保每次页面更改时都会调用 useEffect,那么您可以获取路径值并将其作为依赖项传递给 useEffect。

获取当前路径:

let pathname = useLocation().pathname

将路径名放入你的 useEffect 依赖数组中:

 useEffect(() => 
    const unsubscribe = onAuthStateChanged(
      auth,
      (user) => 
        
        setCurrentUser(user);
        setIsInitialized(true);
      ,
    );

    return unsubscribe;
  , [pathname]);

【讨论】:

感谢您的建议。我尝试了您的解决方案,但仍然无法持续触发。我添加了一些控制台日志,以了解更改将如何影响事情。我已经用 console.log 添加和控制台输出更新了我的帖子。

以上是关于Firebase onauthstatechanged 在页面加载时触发不一致的主要内容,如果未能解决你的问题,请参考以下文章

firebase.auth().onAuthStateChanged 不工作

如何从 firebase.auth().onAuthStateChanged 中提取数据

.onAuthStateChanged 方法是不是计入 Firebase 身份验证验证?

TypeError: firebase.auth(...).onAuthStateChanged 不是函数

在哪里放置 firebase.auth().onAuthStateChanged()

使用 useEffect 与 onAuthStateChanged 检测 Firebase 用户更改 - 有啥区别?