Main Concern
I currently have an Auth Provider set up in my application that wraps around the entire _app.tsx file. This allows me to utilize the "useAuth" hook and access the user object from any part of the app. However, I am facing an issue when using this hook to conditionally load the Navbar component. During the initial few seconds of page load, the user object is not yet available, causing a brief flash of the logged out component on the screen before the correct component is loaded. I have researched various methods such as GetInitialProps and GetServerSideProps to address this problem, but I'm unsure about the most effective solution.
[Update] - Additional Question
- How can I ensure that a Firebase user is fully loaded before a component renders in Next.js?
Technology Stack Utilized
- Next.js
- Firebase Authentication
- MongoDB
Visual Representation
View the gif demonstrating the flashing Navbar
Code Snippets
_app.tsx
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<Global
styles={css`
button {
border: none;
}
input {
border: none;
}
`}
/>
<ThemeProvider theme={theme}>
<CSSReset />
<AuthProvider>
<Page>
<Navbar />
<Component {...pageProps} />
<Footer />
</Page>
</AuthProvider>
</ThemeProvider>
</>
);
}
export default MyApp;
AuthContext.tsx
import React, { useState, useEffect, useContext, createContext } from 'react';
import nookies from 'nookies';
import { firebase } from './initFirebase';
import initFirebase from './initFirebase';
import { auth } from 'firebase';
const AuthContext = createContext<{ user: firebase.User | null }>({
user: null,
});
export function AuthProvider({ children }: any) {
const [user, setUser] = useState<firebase.User | null>(null);
useEffect(() => {
return firebase.auth().onIdTokenChanged(async (user) => {
if (!user) {
setUser(null);
nookies.set(undefined, 'token', '', {});
return;
}
const token = await user.getIdToken();
setUser(user);
setLoading(false);
nookies.set(undefined, 'token', token, {});
});
}, []);
return (
<AuthContext.Provider value={{ user }}>{children}</AuthContext.Provider>
);
}
// signOut function
export const signOut = async () => {
await auth().signOut();
};
// useAuth hook
export const useAuth = () => {
return useContext(AuthContext);
};
Navbar component
const Navbar = () => {
const { user} = useAuth();
const [loggedOut, setLoggedOut] = useState(false);
const toast = useToast();
const router = useRouter();
const { isOpen, onOpen, onClose } = useDisclosure();
const handleSignOut = () => {
signOut()
.then((result) => {
toast({
title: 'Signed Out',
description: 'You have successfully signed out of your account.',
status: 'success',
duration: 4000,
isClosable: true,
position: 'top',
});
setLoggedOut(true);
router.push('/');
})
.catch((error) => {
var errorCode = error.code;
var errorMessage = error.message;
toast({
title: errorCode,
description: errorMessage,
status: 'error',
duration: 4000,
isClosable: true,
position: 'top',
});
});
};
return (
<>
<NavbarWrapper>
<NavbarElementWrapper>
<LogoLink>
<Link href='/'>
<Logo />
</Link>
</LogoLink>
</NavbarElementWrapper>
{user ? (
<SplitLinks>
<NavbarElementWrapper>
<TimelineModal />
</NavbarElementWrapper>
<Menu>
<Tooltip label='Account Details' aria-label='account details'>
<MenuButton style={{ outline: 'none' }}>
<Avatar
src={user.photoURL}
name={user.displayName || user.email.split('@')[0]}
/>
</MenuButton>
</Tooltip>
<MenuList>
<MenuGroup>
<MenuItem onClick={() => onOpen()} height='100%'>
<Box as={FaUser} mr='12px' />
Account
</MenuItem>
<MenuItem onClick={() => router.push('/timelines')} height='100%'>
<Box as={MdTimeline} mr='12px' />
Timelines
</MenuItem>
<MenuDivider />
<MenuItem justifyContent='center' style={{ background: 'none' }}>
<Button
onClick={handleSignOut}
leftIcon='arrow-forward'
variantColor='red'
>
Sign Out
</Button>
</MenuItem>
</MenuGroup>
</MenuList>
</Menu>
</SplitLinks>
) : (
<SplitLinks>
<LinkHoverWrapper first={true}>
<LoginModal />
</LinkHoverWrapper>
<LinkHoverWrapper>
<RegistrationModal />
</LinkHoverWrapper>
</SplitLinks>
)}
<AccountModal isOpen={isOpen} onClose={onClose} />
</NavbarWrapper>
</>
);
};
export default Navbar;