如何解决Next.js中的水合错误
#javascript #网络开发人员 #react #nextjs

如果您最近从使用react到next.js过渡,则必须遇到此错误“ 错误:水合失败,因为初始UI与服务器上呈现的内容不匹配 ”。让我们了解为什么通常会发生此错误以及如何解决该错误。

什么是水合?

在Next.js中,页面的第一个渲染是在服务器上进行的。然后将此预渲染的HTML提供给客户。在客户端,React接管此HTML页面以使此页面交互式(基本上它将事件处理程序连接到DOM节点)。这是水合。

为什么发生此错误?

在此过程中,React还具有预渲染的HTML外观的模型(以便它可以将事件处理程序连接到必要的JS交互性),如果初始渲染的客户端版本与pre-pre-pre-pre-pre-从服务器渲染HTML,我们得到此错误。

某些情况可能会出现此错误:

  • 您使用浏览器API来有条件地渲染组件/页面:在这种情况下,由于浏览器API(LocalStorage,SessionStorage)在服务器上不可用,因此预渲染的HTML可以不同客户端。

    // I will use this atom in the solutions section :)
    const defaultState={
        username:"",
        id:"",
        isLoggedIn:false      
    }
    
    export const adminState=atom<IAdmin>({
        key:"currentAdmin",
        default:  (typeof window !== 'undefined') ? (
            localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user') as string) : defaultState
        ) : defaultState
    })
    //if this state is used to conditionally render, it will cause hydration error
    

    在组件中使用此后坐状状态时,将出现水合错误,因为在服务器上,HTML将根据默认状态呈现HTML,并且在客户端端上,由于localStorage,HTML会有所不同。

  • 您的HTML语法不是正确的:在这种情况下,您可能以某种怪异的方式使用了JSX。一些例子:

    <p><div>logout</div></p>
    
    <p><p>Your text</p></p>
    
  • 一些浏览器扩展改变客户端的HTML。

解决此错误的解决方案:

  • 使用效果的重新配置逻辑:

    服务器在呈现服务器端的HTML时忽略了使用效应块。使用效果仅在客户端运行,并且可以访问所有浏览器API。

    // I am using the above mentioned state atom
    const RequireAuth: React.FC<{ children: ReactElement }> = ({ children }) => {
      const [loader, setLoader] = useState(true);
      const user = useRecoilValue(userState);
      const router = useRouter();
      useEffect(() => {
        if (user.isLoggedIn) {
          setLoader(false);
        } else router.replace('/login');
      }, []);
    
      return <>{!loader && children}</>;
    };
    // As  user.isLoggedIn depends on localStorage,
    // we check for it in the useEffect (client side)
    
  • 在某些组件上禁用SSR,在客户端可能有所不同

    import dynamic from 'next/dynamic';
    //code for YourComponent
    export default dynamic(() => Promise.resolve(YourComponent), { ssr: false });
    
  • 对于时间戳,预渲染的HTML将与客户端版本不同。在这种情况下,您可以通过在元素中使用抑制水力= {true}来抑制警告。

其他建议:而不是在客户端渲染整个页面,而是考虑将需要客户端渲染的零件抽象成单独的组件。利用上述方法渲染它们,从您的末端进行了一些调整。

关于安全性的注释:在上面的文章中,我将JWT令牌存储在LocalStorage中,这不是一个好的做法。更好的解决方案可能包括使用cookie进行身份验证。如果您使用的是cookie,则可以尝试使用getServersideProps()在服务器端获得身份验证状态本身。

Twitter上与我联系| Linkedin | Github

愉快的编码! ðð