我正在将React与Firebase一起使用来开发一个小型Web应用程序。为了进行身份验证,我使用了上下文API,并且在上下文中添加了登录用户的详细信息。
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> );
用户已经登录,并且会话持久性设置为本地。问题是,当我尝试本地主机/订阅(这是一条私有路由)时,context.isAuthenticated为false,因为尚未触发“ onAuthStateChanged”观察者,因此它进入了登录页面,但是在几毫秒内,触发了authStateChange并设置了上下文,但它没有用,因为privateroute认为用户未登录,因此应用程序已经导航到登录状态。我想了解如何解决此问题的知识。
页面加载后,Firebase将从本地存储中还原用户的凭据,并与服务器一起检查它们是否仍然有效。由于这是对服务器的调用,因此可能要花费一些时间并异步发生。这是正常的原因firebase.auth().currentUser是null,直到onAuthStateChanged火灾。
firebase.auth().currentUser
null
onAuthStateChanged
你的问题是,有多种原因firebase.auth().currentUser可能是null:
您要在情况2和3而不是情况1下导航。
典型的解决方案是在第一次onAuthStateChanged触发之前不处理导航。届时,您可以确定已针对服务器检查了凭据,或者没有要检查的凭据,在这两种情况下,您都需要导航至登录页面。
加快速度的另一种方法是,当用户首次登录时,自己将一个小令牌存储在本地存储中,然后在应用加载时读取该令牌。如果存在令牌,则可以区分情况1和情况3,并用它稍早导航到正确的页面。
有关此示例,请参见有关架构移动Web应用程序的 I / O讨论。