I am moving the project from react to nextjs and have a problem. I have a global wrapper that handles the login context and is using local storage. I set "use client" directive at the top, but the component is trying to be rendered on the server too, and this code gives a hydration error:
export function AuthProvider(props) {
const initialParsedToken = typeof localStorage !== 'undefined' ? localStorage.getItem("jwt") : null
if (initialParsedToken) {
const decodedToken = jwtDecode<JwtInterface>(initialParsedToken);
if (decodedToken.exp * 1000 < Date.now()) {
localStorage.removeItem("token");
} else {
initialState.user = decodedToken;
}
}
const [state, dispatch] = useReducer(authReducer, initialState);
if (typeof localStorage === 'undefined'){
return null
}
const login = (userData: any) => {
localStorage.setItem("jwt", userData.token);
dispatch({ type: "LOGIN", payload: userData });
};
function logout() {
localStorage.removeItem("jwt");
dispatch({ type: "LOGOUT" });
}
return (
<AuthContext.Provider
value={{ user: state.user, login, logout }}
{...props}
/>
);
}
The error:
Hydration failed because the initial UI does not match what was rendered on the server.
How can i use the useReducer hook in next, or make auth logic work with this framework?
I was planning to make render static content as server components, having client parts, but can I avoid using ugly useffect hack in each client component and just add a use client in nested dynamic content?
I tried using useffect hack in parent component, but nested components still don't work.
You should check on the type of window instead to make sure you are on the client side
if (typeof window !== undefined){
....your local storage logic
}
Also, you should consider moving your token to cookies instead of local storage as it doesn't cause conflicts and you can check your cookies on the server side using the context parameter, see this