如何解决Next js 错误“警告:预期服务器 HTML 在 <div> 中包含匹配的 <button>”
我有一个黑暗模式组件,它可以在太阳和月亮图标之间进行简单的切换。
DarkMode.tsx
import { observer } from 'mobx-react'
import { MoonIcon,SunIcon } from '@heroicons/react/solid'
import { useStore } from '@/store/index'
export const DarkMode = observer(() => {
const { theme,setTheme,isPersisting } = useStore()
if (!isPersisting) return null
return (
<>
{theme === 'dark' && (
<button
className="fixed bottom-12 right-12 focus:outline-none"
title="Activate light mode"
onClick={() => {
setTheme('light')
}}
>
<MoonIcon className="w-8 h-8" />
</button>
)}
{theme === 'light' && (
<button
className="fixed bottom-12 right-12 focus:outline-none"
title="Activate dark mode"
onClick={() => {
setTheme('dark')
}}
>
<SunIcon className="w-8 h-8" />
</button>
)}
</>
)
})
我正在使用 MobX 跟踪我的 theme
和 mobx-persist-store
以将数据保存在 localStorage
中。
store.ts
import { makeObservable,observable,action } from 'mobx'
import { makePersistable,isPersisting,clearPersistedStore } from 'mobx-persist-store'
import type { Theme,IStore } from '@/types/index'
const name = 'Store'
const IS_SERVER = typeof window === 'undefined'
export class Store implements IStore {
theme: Theme = 'light'
constructor() {
makeObservable(this,{
theme: observable,setTheme: action.bound,reset: action.bound,})
if (!IS_SERVER) {
makePersistable(this,{ name,properties: ['theme'],storage: window.localStorage })
}
}
setTheme(theme: Theme) {
this.theme = theme
}
get isPersisting() {
return isPersisting(this)
}
async reset() {
if (!IS_SERVER) await clearPersistedStore(this)
}
}
当用户在暗模式组件中选择 dark
主题时,我将 html
类添加到 dark
。
_app.tsx
import React from 'react'
import { AppProps } from 'next/app'
import Head from 'next/head'
import { observer } from 'mobx-react'
import useSystemTheme from 'use-system-theme'
import { useStore } from '@/store/index'
import '@/components/NProgress'
import 'nprogress/nprogress.css'
import '@/styles/index.css'
const MyApp = ({ Component,pageProps }: AppProps) => {
const systemTheme = useSystemTheme()
const { theme,setTheme } = useStore()
React.useEffect(() => {
const isDarkTheme = theme === 'dark' || (systemTheme === 'dark' && theme !== 'light')
if (isDarkTheme) {
document.documentElement.classList.add('dark')
setTheme('dark')
} else {
document.documentElement.classList.remove('dark')
setTheme('light')
}
},[theme,systemTheme])
return (
<>
<Component {...pageProps} />
</>
)
}
export default observer(MyApp)
我仍然收到一条错误消息:
VM356 main.js:16820 Warning: Expected server HTML to contain a matching <button> in <div>.
at button
at wrappedComponent (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624277701361:2690:73)
at Nav (http://localhost:3000/_next/static/chunks/pages/tutorial/the-complete-guide-to-starting-a-blog-in-nextjs-and-mdx.js?ts=1624277701361:12454:23)
at Tutorial (http://localhost:3000/_next/static/chunks/pages/tutorial/the-complete-guide-to-starting-a-blog-in-nextjs-and-mdx.js?ts=1624277701361:12973:24)
at MDXLayout
at http://localhost:3000/_next/static/chunks/pages/tutorial/the-complete-guide-to-starting-a-blog-in-nextjs-and-mdx.js?ts=1624277701361:7880:30
at Mdxcontent (http://localhost:3000/_next/static/chunks/pages/tutorial/the-complete-guide-to-starting-a-blog-in-nextjs-and-mdx.js?ts=1624277701361:22563:25)
at wrappedComponent (http://localhost:3000/_next/static/chunks/pages/_app.js?ts=1624277701361:2690:73)
at ErrorBoundary (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:767:47)
at ReactDevOverlay (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:883:23)
at Container (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:8756:5)
at AppContainer (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:9244:24)
at Root (http://localhost:3000/_next/static/chunks/main.js?ts=1624277701361:9380:25)
button
的 onClick
事件处理程序从 DOM 本身消失。
有趣的是它过去可以在 MacOS 上运行,但不能在 Windows 上运行。我克隆了同一个项目。有什么问题?
解决方法
在服务器上,您的 DarkMode
组件不会呈现任何内容(因为 isPersisting
为 false)。然后在客户端,它在第一次通过时呈现一些东西(isPersisting
在客户端呈现时变为真),这就是 React(不是 Next.js)抱怨 SSR 和 CSR 之间的标记不匹配的原因。
基本上这意味着你总是需要用 SSR 渲染一些主题,但 SSR 不知道 localStorage
所以它只能选择默认值。然后在客户端渲染后从 localStorage
中选择正确的值。
如果您想使用 SSR 呈现正确的主题,而不会闪现旧主题或没有类似错误,那么您需要将其存储在 cookie 中。
,拼图的缺失部分是我将 Nav
包裹在 ThemeProvider
外面。
Nav
包含 DarkMode
,因此无法访问 ThemeProvider
。我的 _document.tsx
看起来像:
<Nav />
<ThemeProvider attribute="class" themes={['light','dark']}>
<Component {...pageProps} />
</ThemeProvider>
所以我必须将 Nav
放入 ThemeProvider
中才能使其正常工作。
<ThemeProvider attribute="class" themes={['light','dark']}>
<Nav />
<Component {...pageProps} />
</ThemeProvider>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。