Next.js - 预期的服务器 HTML 在 <div> 中包含匹配的 <div>
Posted
技术标签:
【中文标题】Next.js - 预期的服务器 HTML 在 <div> 中包含匹配的 <div>【英文标题】:Next.js - Expected server HTML to contain a matching <div> in <div> 【发布时间】:2021-08-19 12:20:29 【问题描述】:提供实时示例here
我正在尝试制作一个基本布局,在该布局中,在手机上仅显示最新帖子。在桌面上,左栏应该是帖子,右栏应该是热门类别和最受欢迎的帖子。
这是布局:
const IndexLayout: React.FC<IndexLayoutProps> = () =>
const cols = useScreenType()
return cols === '2-cols' ? (
<div className="w-full flex justify-between items-start">
<ListPosts data-comp="ListPosts" className="w-4/6" />
<div className="sticky ml-12 w-2/6 flex flex-col">
<TopCategories data-comp="TopCategories" className="w-full" />
<PopularPosts data-comp="PopularPosts" className="mt-4" />
</div>
</div>
) : (
<ListPosts data-comp="ListPosts" className="w-full" />
)
这是useScreenType
钩子:
import useMediaQuery from 'react-responsive'
export const useScreenType = () =>
const is2Cols = useMediaQuery( minWidth: 1300 )
const is1Cols = useMediaQuery( minWidth: 800 )
if (is2Cols)
return '2-cols'
if (is1Cols)
return '1-cols'
return 'fullscreen'
我不断收到此错误:
Warning: Expected server html to contain a matching <div> in <div>.
div
ListPosts@webpack-internal:///./components/posts/ListPosts.tsx:31:19
div
IndexLayout@webpack-internal:///./components/layout/IndexLayout.tsx:28:149
div
Index@webpack-internal:///./pages/index.tsx:24:149
ApolloProvider@webpack-internal:///./node_modules/@apollo/client/react/context/ApolloProvider.js:13:18
s@webpack-internal:///./node_modules/next-apollo/dist/index.es.js:26:1911
div
div
MyApp@webpack-internal:///./pages/_app.tsx:37:19
ErrorBoundary@webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ErrorBoundary.js:23:47
ReactDevOverlay@webpack-internal:///./node_modules/@next/react-dev-overlay/lib/internal/ReactDevOverlay.js:73:20
Container@webpack-internal:///./node_modules/next/dist/client/index.js:155:20
AppContainer@webpack-internal:///./node_modules/next/dist/client/index.js:643:18
Root@webpack-internal:///./node_modules/next/dist/client/index.js:779:19
现在我认为问题是由于useScreenType
钩子无法获得宽度,因为window
未在服务器上定义。但是我该如何解决这个问题?不仅我得到一个错误,而且我的 HTML 呈现奇怪。
最终的渲染结果是这样的(当它渲染为“2-cols”时):
<div class="flex flex-col justify-start items-start w-full">
<div class="mt-6 w-full"></div>
<div class="mt-4 flex items-center cursor-pointer transform transition hover:scale-105 text-sm">
<div class="w-full p-6 rounded-lg flex flex-col dark:bg-gray-800 shadow-md"></div>
<div class="mt-4 p-6 rounded-lg flex flex-col dark:bg-gray-800 shadow-md"></div>
</div>
</div>
注意:我使用的是 Next.js v10.2.0
代码可以在GitHub找到
【问题讨论】:
如您所见,您无法访问服务器上的窗口对象,因此如果您想基于窗口对象进行服务器渲染 - 您必须对这些值进行硬编码 我不确定这是否真的与缺少对window
对象的访问有关,您可能会得到一个实际错误,而不是像现在这样的警告。您可以尝试查看 s-s-r example 的 react-responsive,看看这是否是无效标记的原因。
很可能是由于缺少窗口对象的错误被处理并使用了某种回退,这给我们留下了警告。
【参考方案1】:
如您所见,您无法访问服务器上的窗口对象,因此如果您想基于窗口对象在服务器上渲染某些内容 - 您必须对这些值进行硬编码。
您唯一可以依赖的是请求标头中的用户代理,它可以让您对用户设备有所了解。
例如,您可以通过这种方式在 _app.js 中检测用户设备:
const device = deviceDetector.detect(isServer() ? ctx.req.headers['user-agent'] : window.navigator.userAgent)
deviceDetector 是任何一种基于用户代理的设备检测实现
【讨论】:
是的,如果我的页面是 s-s-r 就可以了。索引页面甚至不是服务器渲染的,这让我感到困惑...... 据我所知,每个页面都是 s-s-r,除非你指定它不是。 我知道这个页面不是服务器端渲染的。那么这个bug的原因是什么呢?【参考方案2】:对于任何想知道我如何解决此问题的人,我放弃了带有逻辑的响应式设计并改用 CSS。这是我的布局后修复(用lg
前缀[documentation] 更改了一些类):
const IndexLayout: React.FC<IndexLayoutProps> = () =>
return (
<div className="mt-12 lg:mt-24 w-5/6 mx-auto flex items-start">
<div className="w-full flex justify-between items-start">
<ListPosts className="lg:w-4/6 w-full" />
<div className="hidden sticky ml-12 w-2/6 lg:flex flex-col">
<TopCategories className="w-full" />
<PopularPosts className="mt-4" />
</div>
</div>
</div>
)
【讨论】:
以上是关于Next.js - 预期的服务器 HTML 在 <div> 中包含匹配的 <div>的主要内容,如果未能解决你的问题,请参考以下文章
Next.js:错误:React.Children.only 预期接收单个 React 元素子项
在 React 中渲染格式化(未缩小的)HTML(带有 Next.js 的 s-s-r)
Next Js 错误:元素类型无效:预期为字符串(对于内置组件)或类/函数(对于复合组件)但得到:未定义
Next js 和 google 分析在 nginx 反向代理后面不起作用