Next js 错误“警告:预期服务器 HTML 在 <div> 中包含匹配的 <button>”

dea*_*904 1 javascript reactjs mobx next.js tailwind-css

我有一个黑暗模式组件,它是太阳和月亮图标之间的简单切换。

暗模式.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>
            )}
        </>
    )
})
Run Code Online (Sandbox Code Playgroud)

我正在使用 MobX 来跟踪我的theme&mobx-persist-store将数据保存在localStorage.

商店.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)
    }
}
Run Code Online (Sandbox Code Playgroud)

当用户在深色模式组件中选择主题时,我将添加dark类。htmldark

_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)
Run Code Online (Sandbox Code Playgroud)

我仍然收到一条错误消息:

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>
            )}
        </>
    )
})
Run Code Online (Sandbox Code Playgroud)

button事件onClick处理程序从 DOM 本身消失。

有趣的是,它过去可以在 MacOS 上运行,但不能在 Windows 上运行。我克隆了同一个项目。有什么问题吗?

Tai*_*uan 6

我在深色/浅色主题实现过程中遇到了同样的问题,但根据deadcoder0904的评论解决了它。
就我而言,该应用程序是使用next-themes库构建的。
由于此错误与SSR有关,因此需要确认组件是否安装在前端侧。

import { useEffect, useState } from 'react';
import { useTheme } from 'next-themes';

const ThemeToggler = () => {
  const { theme, setTheme } = useTheme()
  const [hasMounted, setHasMounted] = useState(false);

  useEffect(() => setHasMounted(true));
  
  // this line is the key to avoid the error.
  if (!hasMounted) return null;

  return (
    <div>
      The current theme is: {theme}
      <button onClick={() => setTheme('light')}>Light Mode</button>
      <button onClick={() => setTheme('dark')}>Dark Mode</button>
    </div>
  )
}

export default ThemeToggler;
Run Code Online (Sandbox Code Playgroud)

希望这对您有帮助。