我正在使用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认为用户未登录,因此应用程序已导航至登录。 我想了解如何解决这个问题。
答案 0 :(得分:5)
页面加载后,Firebase将从本地存储中还原用户的凭据,并与服务器一起检查它们是否仍然有效。由于这是对服务器的调用,因此可能要花费一些时间并异步发生。这就是在firebase.auth().currentUser
触发之前,null
是onAuthStateChanged
的原因。
您遇到的问题是,firebase.auth().currentUser
可能是null
的原因有多种:
firebase.auth().currentUser
是null
,因为客户端刚刚加载并且仍在与服务器一起检查凭据。firebase.auth().currentUser
为null
,因为客户端已针对服务器检查了凭据,并且客户端未登录。firebase.auth().currentUser
为null
,因为用户从未登录过,因此没有要检查的凭据。您要在情况2和3中导航,而不是情况1。
典型的解决方案是直到onAuthStateChanged
首次触发后才处理导航。到那时,您可以确定已针对服务器检查了凭据,或者没有要检查的凭据,在两种情况下,您都希望导航到登录页面。
加快此速度的另一种方法是,当用户首次登录时,自己将一个小令牌存储在本地存储中,然后在应用加载时读取该令牌。如果存在令牌,则可以区分情况1和情况3,并用它稍早导航到正确的页面。
有关此示例,请参阅有关Architecting Mobile Web Apps的I / O讨论。