我试图在反应上下文中存储一些有关客户端的基本(非敏感)用户信息,例如他们的显示名称和个人资料图片。该信息存储在本地存储中,以便即使客户端关闭/重新加载页面,它也会持续存在,并且上下文从那里获取数据,并将其显示在客户端组件中。从功能上来说,它工作得很好。但是,我遇到了一些问题:
为了避免水合不匹配错误,我必须禁用整个组件的 SSR。
即使我保持启用 SSR,即使我构建了应用程序,内容也比其他客户端组件需要更多的时间来完成。这种布局转变真的很困扰我。
我的问题是,如何才能让这些信息立即显示出来?我还没有看到其他人遇到这个问题。如果我存储的方式错误,正确的方式是什么?
我已经创建了该错误的“最小工作示例”。我尝试查找在 nextjs 中使用 React context 的各种方法,但它们似乎都表明我做错了什么。我还尝试对 next-themes 进行逆向工程以弄清楚它是如何工作的,但我不太理解它。 任何帮助将不胜感激!
AuthContext
组件及其与 Next.js 中服务器端渲染的交互。
问题的出现是因为服务器和客户端之间的userData
值不同,导致水合失败。发生此失败的原因是 React API
hydrateRoot()
希望渲染的内容与服务器渲染的 HTML 相匹配。您可以参考React文档和中间的“陷阱”部分了解更多细节。建议避免在渲染逻辑中直接使用 typeof window !== 'undefined'
等检查或访问
localStorage
等仅限浏览器的 API,因为这可能会导致服务器和客户端快照之间不匹配。这是我的解决方法:
使用默认值
useState
null
钩子,以确保初始渲染一致。组件安装后,使用 useEffect
userData
。删除动态导入并直接导入ClientComponent
AuthContext
组件:
import React, { createContext, useContext, useEffect, useState, Dispatch, SetStateAction } from "react";
export default function AuthContext({
children,
}: {
children: React.ReactNode;
}) {
const [user, setUser] = useState<string | null>(null); // Initialize with null
// update after component mount
useEffect(() => {
const userData = getUser();
if(userData){
setUser(userData);
}
}, []);
return (
<Context.Provider value={{ userData: user, setUserData: setUser }}>
{children}
</Context.Provider>
);
}
export const useAuthContext = () => useContext(Context);
const getUser = () => {
if(typeof window === 'undefined') return null; // Check for server-side rendering
let user;
try {
user = localStorage.getItem("user");
if (!user) {
user = "I want this to be rendered instantly too!";
localStorage.setItem("user", user);
return user;
}
} catch (e) {}
return user;
};
请注意,这种方法可能会使水合速度变慢,因为您的组件必须渲染两次。干扰慢速连接的用户体验。 JavaScript 代码的加载可能会比初始 HTML 渲染晚得多,因此在水合后立即渲染不同的 UI 也可能会让用户感到不舒服。
但是,您不应该在渲染逻辑中访问仅限浏览器的 API。 希望这对您有用。