加载页面时 Firebase auth.currentUser 为空,当页面加载几毫秒后调用 authstatechange 时用户加载 [英] Firebase auth.currentUser is null when loading the page, user loaded when authstatechange called after some milli seconds of page load

查看:20
本文介绍了加载页面时 Firebase auth.currentUser 为空,当页面加载几毫秒后调用 authstatechange 时用户加载的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 React 和 Firebase 来开发一个小型网络应用程序.为了进行身份验证,我使用了上下文 API,并在上下文中添加了登录用户的详细信息.

<块引用>

AuthProvider.tsx

const AuthProvider: React.FC = props =>{const [state, setState] = useState(authInitialState);console.log("内部 AuthProvider");useEffect(() => {auth.onAuthStateChanged( 用户 => {console.log("Auth State 已更改,用户为 ----->", user);如果(用户){console.log("用户值更新到上下文")设置状态({...authInitialState,isAuthenticated:!!用户,权限:[],用户:用户});}const stateChange = {...authInitialState,isAuthenticated: !!用户,用户};//如果(!用户){返回 setState(stateChange);//}});}, []);console.log("渲染 AuthProvider", state);返回 (<AuthContext.Provider value={state}>{props.children}</AuthContext.Provider>);};导出默认 AuthProvider;

<块引用>

AuthConsumer.tsx

const withAuthContext = (组件:React.ComponentClass|React.FunctionComponent) =>{console.log("渲染 AuthConsumer 为");返回(道具:任何)=>(<AuthContext.Consumer>{上下文=><组件{...props} context={context}/>}</AuthContext.Consumer>);};使用AuthContext导出默认值;

<块引用>

PrivateRoute.tsx

interface PrivateRouteProps 扩展了 RouteProps {//tslint:disable-next-line:no-any组件:任何;上下文:IAuthContext;}const PrivateRoute = (props: PrivateRouteProps) =>{const { 组件:组件,上下文:IAuthContext,...rest } = props;console.log("私有路由", props);返回 (<路线{...休息}render={(routeProps) =>props.context.isAuthenticated ?(<组件{...routeProps}/>) : (<重定向到={{路径名:'/登录',状态:{来自:routeProps.location}}}/>)}/>);};导出默认 withAuthContext(PrivateRoute);

<块引用>

App.tsx

返回(<浏览器路由器><div><开关><PublicRoute path="/frame" component={Frame} 精确 isAuthorized={true}/><Route path="/login" component={NewLogin} 确切的 isAuthorized={true}/><PrivateRoute path="/nav" component={NavigationBar} 精确/><PrivateRoute path="/dashboard" component={AnalyticsDashBoard} 精确/><PrivateRoute path="/subscription" component={OrderSuccess} 精确/><PrivateRoute path="/onboarding" component={OnBoarding} 精确/></开关>

</BrowserRouter>);

用户已经登录,会话持久化设置为本地.问题是,当我尝试 localhost/subscription,这是一个私有路由时,context.isAuthenticated 是假的,因为onAuthStateChanged"观察者还没有被触发,所以它会进入登录页面,但在几毫秒内,authStateChange 被触发并设置了上下文,但它没有用,因为应用程序已经导航到登录,因为 privateroute 认为用户没有登录.我想了解如何解决这个问题.

解决方案

当页面加载时,Firebase 从本地存储恢复用户的凭据,并与服务器检查它们是否仍然有效.由于这是对服务器的调用,因此可能需要一些时间并且异步发生.这就是 firebase.auth().currentUsernull 直到 onAuthStateChanged 触发是正常的原因.

您遇到的问题是有多种原因 firebase.auth().currentUser 可以为 null:

  1. firebase.auth().currentUsernull 因为客户端刚刚加载并且它仍在与服务器检查凭据.
  2. firebase.auth().currentUsernull 因为客户端根据服务器检查了凭据并且客户端未登录.
  3. firebase.auth().currentUsernull 因为用户从未登录,所以没有要检查的凭据.

您想在情况 2 和 3 中导航,但不想在情况 1 中导航.

典型的解决方案是在第一次 onAuthStateChanged 触发之前不处理导航.此时,您可以确定已针对服务器检查了凭据,或者没有要检查的凭据,在这两种情况下,您都需要导航到登录页面.

<小时>

加速这一过程的另一个选项是在用户第一次登录时自己在本地存储中存储一个小令牌,然后在应用加载时读取该令牌.如果令牌存在,您可以区分情况 1 和 3,并使用它来更快地导航到正确的页面.

有关此示例,请参阅有关架构移动网络应用的 I/O 讨论.

I am using React with Firebase to develop a small web app. To take care of authentication, I am using context API and inside context I am adding loggedin user details.

AuthProvider.tsx

const AuthProvider: React.FC = props => {
  const [state, setState] = useState<IAuthContext>(authInitialState);
  console.log("Inside AuthProvider");
  useEffect(() => {
    auth.onAuthStateChanged( user => {
      console.log("Auth State changed and user is ----->", user);
      if (user) {
        console.log("User value updated to the context")
        setState({
            ...authInitialState,
          isAuthenticated:!!user,
          permissions:[],
          user:user
        });
      }
      const stateChange = {
        ...authInitialState,
        isAuthenticated: !!user,
        user
      };
      // if (!user) {
        return setState(stateChange);
      // }
    });
  }, []);
  console.log("Rendering AuthProvider", state);
  return (
    <AuthContext.Provider value={state}>{props.children}</AuthContext.Provider>
  );
};
export default AuthProvider;

AuthConsumer.tsx

const withAuthContext = (
  Component: React.ComponentClass<any> | React.FunctionComponent<any>
) => {
  console.log("Rendering AuthConsumer for ");
  return (props: any) => (
    <AuthContext.Consumer>
      {context => <Component {...props} context={context} />}
    </AuthContext.Consumer>
  );
};

export default withAuthContext;

PrivateRoute.tsx

interface PrivateRouteProps extends RouteProps {
    // tslint:disable-next-line:no-any
    component: any;
    context: IAuthContext;
}

const PrivateRoute = (props: PrivateRouteProps) => {
    const { component: Component, context: IAuthContext, ...rest } = props;
    console.log("Private route for ", props);
    return (
        <Route
            {...rest}
            render={(routeProps) =>
                props.context.isAuthenticated ? (
                    <Component {...routeProps} />
                ) : (
                    <Redirect
                        to={{
                            pathname: '/login',
                            state: { from: routeProps.location }
                        }}
                    />
                )
            }
        />
    );
};

export default withAuthContext(PrivateRoute);

App.tsx

return (
        <BrowserRouter>
            <div>
                <Switch>
                    <PublicRoute path="/frame" component={Frame} exact isAuthorized={true}/>
                    <Route path="/login" component={NewLogin} exact isAuthorized={true}/>
                    <PrivateRoute path="/nav" component={NavigationBar} exact/>
                    <PrivateRoute path="/dashboard" component={AnalyticsDashBoard} exact/>
                    <PrivateRoute path="/subscription" component={OrderSuccess} exact/>
                    <PrivateRoute path="/onboarding" component={OnBoarding} exact/>
                </Switch>
            </div>
        </BrowserRouter>
    );

The user is already loggedin, and session persistence set to local. The problem is, when I try localhost/subscription, which is a private route, context.isAuthenticated is false since "onAuthStateChanged" observer is not triggered yet, so its going to login page, but in few milliseconds, authStateChange triggered and context set, but its no use since application already navigated to login since privateroute thought user not loggedin. I would like to get knowledge on how to overcome this issue.

解决方案

When the page loads Firebase restores the user's credentials from local storage and checks with the server whether they are still valid. Since this is a call to the server, it may take some time and happens asynchronously. This is the reason it is normal that firebase.auth().currentUser is null until onAuthStateChanged fires.

The problem you have is that there are multiple reasons firebase.auth().currentUser can be null:

  1. firebase.auth().currentUser is null because the client just loaded and it's still checking the credentials with the server.
  2. firebase.auth().currentUser is null because the client checked the credentials against the server and the client isn't logged in.
  3. firebase.auth().currentUser is null because the user never signed in, so there are no credentials to check.

You want to navigate in case 2 and 3, but not case 1.

The typical solution is to not handle navigation until the first time onAuthStateChanged has fired. At that point you can be certain that the credentials were checked against the server, or that there were no credentials to check, in both of which cases you'll want to navigate to the sign-in page.


An additional option to speed this up is to store a small token in local storage yourself when the user signs in for the first time, and then read that when the app loads. If the token is present you can distinguish between case 1 and 3, and use that to navigate to the correct page a bit sooner.

For an example of this, see this I/O talk about Architecting Mobile Web Apps.

这篇关于加载页面时 Firebase auth.currentUser 为空,当页面加载几毫秒后调用 authstatechange 时用户加载的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆