英文:
How to fix the Hydration error when using localStorage with Next.js?
问题
我尝试将上下文保存在localStorage
中在Next.js项目中,但刷新页面时出现了水合错误。
如何解决这个问题?
type AppState = {
name: string;
salary: number;
info: {
email: string;
department: string;
};
};
interface Props {
children: ReactNode;
}
const initialState: AppState = {
name: "osamu1908",
salary: 2000,
info: {
email: "dynamic@gmail.com",
department: "software developer",
},
};
export const AppState = createContext<AppState>(initialState);
export const AppStateSet = createContext<Dispatch<SetStateAction<AppState>>>(
() => {}
);
const initalFromLocalStorage = () => {
if (typeof window !== "undefined") {
const result = localStorage.getItem("state");
if (result) {
return JSON.parse(result) as AppState;
}
}
return initialState;
};
const AppStateProvider = ({ children }: Props) => {
const [state, setState] = useState<AppState>(initalFromLocalStorage());
useEffect(() => {
localStorage.setItem("state", JSON.stringify(state));
}, [state]);
return (
<AppState.Provider value={state}>
<AppStateSet.Provider value={setState}>{children}</AppStateSet.Provider>
</AppState.Provider>
);
};
export default AppStateProvider;
英文:
I'm trying to persist context in localStorage
in a Next.js project, but I get a hydration error when I refresh the page.
How can I solve this problem?
type AppState = {
name: string;
salary: number;
info: {
email: string;
department: string;
};
};
interface Props {
children: ReactNode;
}
const initialState: AppState = {
name: "osamu1908",
salary: 2000,
info: {
email: "dynamic@gmail.com",
department: "software developer",
},
};
export const AppState = createContext<AppState>(initialState);
export const AppStateSet = createContext<Dispatch<SetStateAction<AppState>>>(
() => {}
);
const initalFromLocalStorage = () => {
if (typeof window !== "undefined") {
const result = localStorage.getItem("state");
if (result) {
return JSON.parse(result) as AppState;
}
}
return initialState;
};
const AppStateProvider = ({ children }: Props) => {
const [state, setState] = useState<AppState>(initalFromLocalStorage());
useEffect(() => {
localStorage.setItem("state", JSON.stringify(state));
}, [state]);
return (
<AppState.Provider value={state}>
<AppStateSet.Provider value={setState}>{children}</AppStateSet.Provider>
</AppState.Provider>
);
};
export default AppStateProvider;
答案1
得分: 1
以下是您要翻译的内容:
"初始渲染应该在客户端和服务器上保持一致。任何时候违反此规则,都会出现hydration错误,就像在您的情况下一样,服务器上没有localStorage
,而在浏览器上存在,导致不同的初始状态。
您应该将实例化主题的逻辑移动到useEffect
内部:
const AppStateProvider = ({ children }: Props) => {
const [state, setState] = useState<AppState | null>(null);
useEffect(() => {
if (state) {
localStorage.setItem("state", JSON.stringify(state));
}
}, [state]);
useEffect(() => {
setState(initalFromLocalStorage());
}, []);
// 这样做是为了在上下文完全设置之前不显示任何内容。您还可以设置一个加载器或其他内容。
if (!state) {
return null;
}
return (
<AppState.Provider value={state}>
<AppStateSet.Provider value={setState}>{children}</AppStateSet.Provider>
</AppState.Provider>
);
};
而且在这一点上,您可以将initalFromLocalStorage
简化如下:
const initalFromLocalStorage = () => {
const result = localStorage.getItem("state");
if (result) {
return JSON.parse(result) as AppState;
}
return initialState;
};
英文:
The initial render should be the same on the client and server. Anytime you violate this rule, you get a hydration error, like in your case, where there will be no localStorage
on the server, and it's present on the browser, leading to a different initial state.
You should move the logic of instantiating the theme inside an useEffect
:
const AppStateProvider = ({ children }: Props) => {
const [state, setState] = useState<AppState | null>(null);
useEffect(() => {
if (state) {
localStorage.setItem("state", JSON.stringify(state));
}
}, [state]);
useEffect(() => {
setState(initalFromLocalStorage());
}, []);
// This is so you don't show anything before the context is fully set. You could also set a loader or something.
if (!state) {
return null;
}
return (
<AppState.Provider value={state}>
<AppStateSet.Provider value={setState}>{children}</AppStateSet.Provider>
</AppState.Provider>
);
};
And at this point, you can simplify initalFromLocalStorage
as below:
const initalFromLocalStorage = () => {
const result = localStorage.getItem("state");
if (result) {
return JSON.parse(result) as AppState;
}
return initialState;
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论