简体   繁体   中英

When the page is reloaded, firebase.auth.currentUser returns null

I'm trying to implement a firebase authentication to my web app with react.

The log-in/out flow is like this.

  • The user who succeeded in sign-in at the SIGN-IN page, redirected to the HOME page.

  • When Non-authenticated user access to the HOME page, redirected to the SIGN-IN page

It is confirmed that the user is redirected to the HOME page from SIGN-IN and the user who clicked "sign out" button at the HOME page is redirected to the SIGN-IN page.

However, the problem is when the authenticated user reloads the page at the HOME page, the user is redirected to the SIGN-IN page and then redirected to the HOME page.

I supposed than the authenticated user isn't redirected to the SIGN-IN page.

How can I fix this problem?

My codes are following:

  • FirebaseContext.tsx
import * as React from "react";
import { createContext, useContext } from "react";
import { firebase } from "../utils";

const FirebaseContext = createContext(firebase);

export const FirebaseProvider: React.FC = props => (
    <FirebaseContext.Provider value={firebase}>
        {props.children}
    </FirebaseContext.Provider>
);

export const useFirebase = () => useContext(FirebaseContext);

  • App.tsx
import * as React from "react";
import { Route, Switch } from "react-router-dom";
import * as routes from "./constants/routes";

import { FirebaseProvider } from "./contexts/FirebaseContext";
import { Home } from "./pages/Home";
import { SignIn } from "./pages/SignIn";

const App: React.FC = () => {
    return (
        <FirebaseProvider>
            <Switch>
                <Route exact={true} path={routes.SIGN_IN} component={SignIn} />
                <Route exact={true} path={routes.HOME} component={Home} />
            </Switch>
        </FirebaseProvider>
    );
};

export default App;
  • AuthUserHook.ts
import { useState, useEffect } from "react";
import { User } from "firebase";


export const useAuthUser = (firebase: any) => {
    const [authUser, setAuthUser] = useState<User | null>(firebase.auth.currentUser);
    useEffect(() => {
        const unsubcribe = firebase.auth.onAuthStateChanged((user: User | null) => setAuthUser(user));
        unsubcribe();
    }, []);

    return authUser;
};
  • SignIn.tsx
import * as React from "react";
import { useHistory } from "react-router-dom";

import { useFirebase } from "../../contexts/FirebaseContext";
import { useAuthUser } from "../../Hooks/AuthUserHook";
import * as routes from "../../constants/routes";
import { SignInForm } from "./SignInForm";


export const SignIn: React.FC = () => {
    const firebase = useFirebase();
    const authUser = useAuthUser(firebase);
    const history = useHistory();

    if (authUser) {
        history.push(routes.HOME);
    }

    return (
        <div>
            <h1>Sign in</h1>
            <SignInForm />
        </div>
    );
};
  • Home.tsx
import * as React from "react";
import { useHistory } from "react-router-dom";
import { useFirebase } from "../../contexts/FirebaseContext";
import { useAuthUser } from "../../Hooks/AuthUserHook";

import * as routes from "../../constants/routes";

export const Home: React.FC = () => {
    const firebase = useFirebase();
    const authUser = useAuthUser(firebase);
    const history = useHistory();

    if (!authUser) {
        history.push(routes.SIGN_IN);
    }

    return (
        <div>
            <h2>Home Page</h2>
            <p>The Home Page is accessible by every signed in user.</p>
        </div>
    );
};

Upon page load the Firebase SDK tries to restore the user's credentials from local storage. As part of this it checks with the server if the user's login session is still valid, and during that time the currentUser is null. Then once the login session is restored, it fires an onAuthStateChanged event with the new state. So you'll need to hook into that to update your state.

The way I usually deal with this is to start on a "log in" screen and then redirect to the main screen when you get a valid user in your auth state change handler.

One extra trick is to store your own flag in local storage when the user has authenticated before, and then read that when the page loads. Since access to this local storage is synchronous, that means you can immediately show the main screen in cases where they've been signed in before. Just keep in mind that your local flag may be wrong (as that's precisely why Firebase has to check with the server), so you should be able to then handle navigating back to the login screen.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM