问题
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.
回答1:
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
:
firebase.auth().currentUser
isnull
because the client just loaded and it's still checking the credentials with the server.firebase.auth().currentUser
isnull
because the client checked the credentials against the server and the client isn't logged in.firebase.auth().currentUser
isnull
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.
来源:https://stackoverflow.com/questions/60149287/firebase-auth-currentuser-is-null-when-loading-the-page-user-loaded-when-authst