admin管理员组文章数量:1291041
I am trying to implement the localstorage
hook in NextJS, but getting the following error: Error: Hydration failed because the initial UI does not match what was rendered on the server.
. Any ideas of what might cause it? To me, it looks like the useEffect
function is not properly used. How do I force the CSR instead of SSR to avoid the hydration error?
import { useState, useEffect } from 'react';
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window === 'undefined') {
return initialValue;
}
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
useEffect(() => {
setStoredValue(storedValue);
}, [storedValue, setStoredValue]);
// eslint-disable-next-line no-unused-vars
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
if (typeof window !== 'undefined') {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue] as const;
}
export default useLocalStorage;
I am trying to implement the localstorage
hook in NextJS, but getting the following error: Error: Hydration failed because the initial UI does not match what was rendered on the server.
. Any ideas of what might cause it? To me, it looks like the useEffect
function is not properly used. How do I force the CSR instead of SSR to avoid the hydration error?
import { useState, useEffect } from 'react';
function useLocalStorage<T>(key: string, initialValue: T) {
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window === 'undefined') {
return initialValue;
}
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
useEffect(() => {
setStoredValue(storedValue);
}, [storedValue, setStoredValue]);
// eslint-disable-next-line no-unused-vars
const setValue = (value: T | ((val: T) => T)) => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
if (typeof window !== 'undefined') {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
} catch (error) {
console.log(error);
}
};
return [storedValue, setValue] as const;
}
export default useLocalStorage;
Share
Improve this question
asked Oct 4, 2022 at 7:42
AleksandrsAleksandrs
1111 silver badge8 bronze badges
2
- Check about this disccussion about the error github./vercel/next.js/discussions/35773 – Rilla Commented Oct 4, 2022 at 13:04
-
You get the hydration error because the initial state you set for the
storedValue
state variable is different on the server and on the client. Try setting the initial state for that variable inside auseEffect
instead. See Warning: Text content did not match. Server: "I'm out" Client: "I'm in" div. – juliomalves Commented Oct 9, 2022 at 20:32
3 Answers
Reset to default 4Try this version of the hook:
import { useEffect, useState } from 'react';
const isServer = typeof window === 'undefined';
export default function useLocalStorage(key, initialValue) {
// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState(() => initialValue);
const initialize = () => {
if (isServer) {
return initialValue;
}
try {
// Get from local storage by key
const item = window.localStorage.getItem(key);
// Parse stored json or if none return initialValue
return item ? JSON.parse(item) : initialValue;
} catch (error) {
// If error also return initialValue
console.log(error);
return initialValue;
}
};
/* prevents hydration error so that state is only initialized after server is defined */
useEffect(() => {
if (!isServer) {
setStoredValue(initialize());
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue = (value) => {
try {
// Allow value to be a function so we have same API as useState
const valueToStore =
value instanceof Function ? value(storedValue) : value;
// Save state
setStoredValue(valueToStore);
// Save to local storage
if (typeof window !== 'undefined') {
window.localStorage.setItem(key, JSON.stringify(valueToStore));
}
} catch (error) {
// A more advanced implementation would handle the error case
console.log(error);
}
};
return [storedValue, setValue];
}
There are two ways which i know to remove Hydration error.
First is to use next/dynamic and load dynamically with ssr option passes as false like this :
import dynamic from 'next/dynamic'
const DynamicHeader = dynamic(() => import('../ponents/header'), {
ssr: false,
})
But as this is a custom hook and we can only import Component dynamically so you have to use useEffect correctly in this case !
Second is to use useEffect hook. I have done this in Js Hope this helps !
import { useEffect, useState } from "react";
export default function useLocalStorage(key, initialValue) {
const [value, setValue] = useState();
useEffect(() => {
const data = localStorage.getItem(key);
if (data === null) {
if (typeof initialValue === "function") {
setValue(initialValue());
} else {
setValue(initialValue);
}
} else {
setValue(JSON.parse(data));
}
}, []);
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [value]);
return [value, setValue];
}
Zustand seems to have a solution for this, im currently using Next 13 app router. i was having similar issues so went to zustand docs, there is property called skipHydration
which can be used to control when the hydration occurs.
what i did was , made a client ponent called Hydrations, and i have imported all my zustand stores and called the rehydrate
function , Zustand docs
Auth slice
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface TypeAuth {
admin: TypeAdmin | null;
setAdmin: (admin: TypeAdmin) => void;
}
const useAuth = create<TypeAuth>()(
persist(
(set) => ({
admin: null,
setAdmin: (admin) => {
set({ admin });
},
}),
{
name: 'auth',
skipHydration: true,
}
)
);
export default useAuth;
Hydration File
'use client';
import { useEffect } from 'react';
import { useAuth } from '@/utils/slices';
export default function Hydrations() {
useEffect(() => {
useAuth.persist.rehydrate();
}, []);
return null;
}
Now just import this Hydration File in your root layout
本文标签: javascriptReact custom localstorage hook hydration error in NextJSStack Overflow
版权声明:本文标题:javascript - React custom localstorage hook hydration error in NextJS - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741501473a2382075.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论