I am building an app that uses react and firebase. I created a login component, where it sets the state "isLoggedIn" to true after they log in and redirects the user to the main page. The problem is that the state is false by default then takes time to set, so when you refresh the main page, you can see the login page briefly. Do I change my router redirect code or set the state differently?
Here is my code:
const [isLoggedIn, setIsLoggedIn] = useState(false)
onAuthStateChanged(auth, (currentUser) => {
setIsLoggedIn(currentUser)
})
return (
<Router>
{isLoggedIn ? <Redirect to="/home" /> : <Redirect to="/login" />}
<Route path="/home" component={SongList} />
<Route path="/login" component={Auth} />
</Router>
)
CodePudding user response:
In react-router-dom v5 the common pattern is to create a custom route component that handles the auth state, even when it's pending. Create an AuthRoute component that starts from an indeterminant state and don't render the route's component or the redirect until you have authentication confirmation.
Example:
const AuthRoute = props => {
const location = useLocation();
const [isLoggedIn, setIsLoggedIn] = useState(); // <-- neither true nor false
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
setIsLoggedIn(currentUser); // sets user object or null
});
return unsubscribe; // <-- cleanup auth subscription!
}, []);
if (isLoggedIn === undefined) return null; // <-- or loading indicator, etc...
return isLoggedIn ? (
<Route {...props} />
) : (
<Redirect
to={{
pathname: "/login",
state: { location }, // <-- pass current location
}}
/>
);
};
Usage:
<Router>
<Switch> // <-- match & render a single route
<AuthRoute path="/home" component={SongList} />
<Route path="/login" component={Auth} />
</Switch>
</Router>
In the Auth component access the passed location from route state and after a successful login you can redirect users back to the route they were originally attempting to access.
const { state } = useLocation();
const history = useHistory();
...
// successful login, redirect back to route, or home if undefined
history.replace(state.location?.pathname ?? "/home")
Having each "instance" of a PrivateRoute maintaining its own state though you'll be doing a lot of auth checks. From here you may want to implement an AuthProvider React context component that stores the isLoggedIn state and each PrivateRoute component subscribes to the context value. This way after a user logs in the app can hold on to this state and not need to recheck/fetch it each time a protected route is accessed.
