I am constantly getting Hydration error with Next Js when I log in with a user, update a context value and re render some components. Specifically my NavBar component. I am using react-bootstrap to render the NavBar.
The code to log in the user has the following form:
export default function LoginPage() {
let [status, setStatus] = React.useState(false);
const {setUser} = React.useContext(UserContext);
function loginUser (uid){
if (firebase.auth().currentUser) {
firebase.auth().currentUser.getIdToken()
.then(idToken => {
(async () => {
var res = await fetch(`${serverUrl}/account/login/${uid}`, {
method: 'GET',
headers: {'Authorization': idToken}
});
var data = await res.json();
let user_data = data[0]
if (user_data) {
console.log(data)
let userValues = {
uid: user_data.uid,
name: user_data.name,
}
setUser(userValues);
localStorage.setItem('userValues', JSON.stringify(userValues));
alert(`Bienvenido ${user_data.name}`);
setStatus(true);
} else {
alert('No se encontró la cuenta')
}
})()
})
}
}
The component that re renders is the NavBar and has the following form:
export default function NavBar() {
const {user, setUser} = React.useContext(UserContext);
const logout = () => {
setUser(false);
localStorage.removeItem('userValues');
}
const renderUserSession = ()=>{
if (user) {
return <span>Usuario: {user.name} <Link href={"/"}><button onClick={logout}>Cerrar sesión</button></Link></span>
} else {
return <Link href={"/account/login"}><button>Iniciar sesión</button></Link>
}
}
return (
<header>
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand href="/">Inicio</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Nav.Link href="/products">Productos</Nav.Link>
<Nav.Link href="/about">Sobre Bebop</Nav.Link>
</Nav>
<Navbar.Text>
{renderUserSession()}
</Navbar.Text>
</Navbar.Collapse>
</Container>
</Navbar>
</header>
)
}
Not sure how the NavBar or any other component should re render when the user logs in to avoid the Hydration error. The error does not appear while the user is logged out.
I have managed to solve the problem as described in Next's documentation page about Hydration errors React Hydration Error by using useEffect. Anyway I am adding my refactored NavBar code to be used as example:
export default function NavBar({firebase}) {
const {user, setUser} = React.useContext(UserContext);
// Added loggedUser State with useState
const [loggedUser, setLoggedUser] = React.useState(' ')
// Added useEffect depending con context user variable
useEffect(() => setLoggedUser(user), [user])
const logout = () => {
firebase.auth().signOut();
setUser(false);
localStorage.removeItem('userValues');
}
return (
<header>
<Navbar bg="light" expand="lg">
<Container>
<Navbar.Brand href="/">Inicio</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav"/>
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="me-auto">
<Nav.Link href="/products">Productos</Nav.Link>
<Nav.Link href="/about">Sobre Bebop</Nav.Link>
<NavDropdown title="Acciones" id="basic-nav-dropdown">
<NavDropdown.Item href="#action/3.1">Gestionar cuenta</NavDropdown.Item>
<NavDropdown.Divider/>
<NavDropdown.Item>
<div onClick={logout}>Cerrar sesión</div>
</NavDropdown.Item>
</NavDropdown>
</Nav>
<Navbar.Text>
{loggedUser && <span>{loggedUser.name}</span>}
</Navbar.Text>
</Navbar.Collapse>
</Container>
</Navbar>
</header>
)
};