I have an application where people can access to the website if not login, however they should be able to access to "/app"
, "/app/*"
only if authenticated. My code below works but for some reason there is half a second, or a second of the content of "/app"
shown before showing the message "you're not allowed to..."
. Any idea why is that?
import { Provider, useDispatch, useSelector } from 'react-redux';
import { useRouter } from 'next/router';
import '../styles/front.css';
import '../styles/app.css';
import React, { useEffect, useState } from 'react';
import { wrapper, store } from '../store';
import { login, logout, selectUser } from "../redux/slices/userSlice";
import { auth } from '../firebase';
function MyApp({ Component, pageProps }) {
const user = useSelector(selectUser);
const dispatch = useDispatch();
const router = useRouter();
const isAppPage = router.pathname.startsWith('/app');
const [shouldRender, setShouldRender] = useState(true); // New state variable
const [loading, setLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((userAuth) => {
if (userAuth) {
dispatch(login({
email: userAuth.email,
uid: userAuth.uid,
displayName: userAuth.displayName,
photoUrl: userAuth.photoURL
}));
setIsAuthenticated(true);
} else {
if (isAppPage) {
setShouldRender(false);
}
dispatch(logout());
setIsAuthenticated(false);
}
setLoading(false); // Set loading to false once the authentication status is checked
});
return () => unsubscribe(); // Cleanup the event listener when the component unmounts
}, []);
return (
<Provider store={store}>
{shouldRender ? <Component {...pageProps} /> : <p>You're not allowed to access that page.</p>}
</Provider>
);
}
export default wrapper.withRedux(MyApp);
You basically need a condition with a third value that is neither "show the content" nor "you can't see this content". Something like a "pending" state that conditionally renders neither the Component
nor the "You're not allowed to access that page."
text.
The initial shouldRender
state matches one of these two states you don't want to immediately render. Start with undefined
and explicitly check for this and conditionally return null or a loading indicator, etc.
Example:
function MyApp({ Component, pageProps }) {
const user = useSelector(selectUser);
const dispatch = useDispatch();
const router = useRouter();
const isAppPage = router.pathname.startsWith('/app');
const [shouldRender, setShouldRender] = useState(); // initially undefined
const [loading, setLoading] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((userAuth) => {
if (userAuth) {
dispatch(login({
email: userAuth.email,
uid: userAuth.uid,
displayName: userAuth.displayName,
photoUrl: userAuth.photoURL
}));
setIsAuthenticated(true);
setShouldRender(true); // <-- show the content
} else {
if (isAppPage) {
setShouldRender(false); // <-- hide content
}
dispatch(logout());
setIsAuthenticated(false);
}
setLoading(false);
});
return () => unsubscribe();
}, []);
if (shouldRender === undefined) {
return null; // or loading indicator/spinner/etc
}
return (
<Provider store={store}>
{shouldRender
? <Component {...pageProps} />
: <p>You're not allowed to access that page.</p>
}
</Provider>
);
}