提问人:Takeshi 251 提问时间:10/20/2023 更新时间:10/23/2023 访问量:72
警告:道具“className”不匹配,并且未在 Next 13 App Router 中定义窗口
Warning: Prop `className` did not match and window is not defined in Next 13 App Router
问:
我在解决Next.js 13应用程序路由器中的错误时感到头疼。
Warning: Prop `className` did not match.
Server: "styled__HeadLink-sc-6589f63-0 fIOFTm"
Client: "styled__HeadLink-sc-b76127ed-0 jzAIeh"
我当然做了很多谷歌搜索,以获取有关如何解决这个问题的信息。错误最令人不快的事情是它们很难被发现。
- 因此,为了解决这个问题,我遵循了在互联网上找到的建议。
我的 .babelrc
{
"env": {
"development": {
"presets": ["next/babel"],
"plugins": [["babel-plugin-styled-components", { "ssr": true, "displayName": true, "preprocess": false }]]
},
"production": {
"plugins": [["babel-plugin-styled-components", { "ssr": true, "displayName": true, "preprocess": false }]],
"presets": ["next/babel"]
},
"test": {
"presets": ["next/babel"]
}
},
"plugins": [["babel-plugin-styled-components", { "ssr": true, "displayName": true, "preprocess": false }]]
}
next.config.js
reactStrictMode: false,
compiler: {
styledComponents: true,
},
我在代码中使用了窗口检查。
export const isClient = () => typeof window !== 'undefined';
我也尝试使用next/dynamic。
export default dynamic(() => Promise.resolve(VeryTroubleBlock), { ssr: false });
似乎我发现了它发生的问题,特别是仅在 Swiper 的初始渲染期间。
const isTablet = useAdaptiveSize(Breakpoints.tablet);
const isMobile = useAdaptiveSize(Breakpoints.mobile);
return (
<div className="here_is_trouble">
{isTablet ? (
<Swiper
modules={[Pagination, A11y]}
pagination={{ clickable: true }}
slidesPerView={isMobile ? 1 : 2}
spaceBetween={24}
>
{managers.map((manager: TroubleBlockProps, index: number) => (
<SwiperSlide key={`${index}`}>
<ManagerItems {...manager} />
</SwiperSlide>
))}
</Swiper>
) : (
<>
{managers.map((manager) => (
<ManagerItems key={manager.name} {...manager} />
))}
</>
)}
</div>
)
export default dynamic(() => Promise.resolve(VeryTroubleBlock), { ssr: false });
我的自定义钩子
import { isClient } from '@/utils/client-check';
import { useState, useLayoutEffect } from 'react';
import { useWindowSize } from 'usehooks-ts';
export function useAdaptiveSize(size: number): boolean {
const { width } = useWindowSize();
const [isTablet, setIsTablet] = useState(false);
useLayoutEffect(() => {
if (isClient()) setIsTablet(width < size);
}, [width]);
return isTablet;
}
有趣的是,我的自定义钩子帮助我解决了应用程序一页上的问题,但是在使用此 Swiper 的第二页上,出现了问题。
当然,有一个解决方案可以消除“警告:Prop className
不匹配”错误。您需要将自定义钩子的执行替换为 innerWidth < Breakpoints.tablet
。这样可以消除控制台错误,但它会出现在服务器上,指示未定义窗口
。
我愿意接受任何批评,并愿意接受帮助来解决这个问题。
祝大家有美好的一天!
答:
0赞
Takeshi 251
10/23/2023
#1
问题: 这个问题源于这样一个事实,即我的全局样式是用 CSS 编写的,而其余的样式是使用 styled-components 实现的。
答:
为了解决这个问题,我选择过渡到使用样式化组件来满足所有样式需求。这统一了样式方法,并确保了服务器端和客户端渲染期间样式的一致性。此外,我删除了在阶段中使用 React.Fragment。<></>
StyleSheetManager
return styles
'use client';
import React, { useState } from 'react';
import { useServerInsertedHTML } from 'next/navigation';
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
export default function StyledComponentsRegistry({ children }: { children: React.ReactNode }) {
// Only create stylesheet once with lazy initial state
// x-ref: https://reactjs.org/docs/hooks-reference.html#lazy-initial-state
const [styledComponentsStyleSheet] = useState(() => new ServerStyleSheet());
useServerInsertedHTML(() => {
const styles = styledComponentsStyleSheet.getStyleElement();
styledComponentsStyleSheet.instance.clearTag();
return styles;
});
if (typeof window !== 'undefined') return <>{children}</>;
return <StyleSheetManager sheet={styledComponentsStyleSheet.instance}>{children}</StyleSheetManager>;
}
评论