next-themes两行代码就能解决主题切换!

next-themes是在next.js应用中解决主题色切换的方案,目前在 github 已经有4.4k star了。

它的用法也很简单,毕竟他的口号就是两行代码完美解决暗色模式,那么现在我们就来看看吧

本文是在 @next14 的环境,即 app router

如果是 page router,请去官网自助食用

用法

首先安装依赖

$ npm install next-themes
# or
$ yarn add next-themes

然后在layout中使用,这里suppressHydrationWarning其实就是帮我们处理添加data-theme,以方便我们切换主题

// app/layout.jsx
import { ThemeProvider } from 'next-themes'

export default function Layout({ children }) {
  return (
    <html suppressHydrationWarning>
      <head />
      <body>
        <ThemeProvider>{children}</ThemeProvider>
      </body>
    </html>
  )
}

// 如果你是 tailwind,那么需要添加 attribute="class" 属性
// 具体参考 https://github.com/pacocoursey/next-themes?tab=readme-ov-file#with-tailwind

接着,我们通过api setTheme切换主题;不难发现,它是通过useTheme()解构出来的,使用很方便

import { useTheme } from 'next-themes'

const ThemeChanger = () => {
  const { theme, setTheme } = useTheme()

  return (
    <div>
      当前主题是: {theme}
      <button onClick={() => setTheme('light')}>亮色模式</button>
      <button onClick={() => setTheme('dark')}>暗色模式</button>
    </div>
  )
}

需要注意的是,当我们setTheme时,将会改变全局的主题色,也就是说其他页面的主题也会被我们影响到,因为上面我们说过,它的原理其实还是在html标签上修改data-theme。那么如果我们希望某个独特的页面固定为暗色主题,该怎么办呢?

下面就要开始介绍ThemeProvider的属性了,这里仅挑选重要的几个属性来讲~

  • storageKey = 'theme': 在 storage 里存储的 key,也就是说当我们 setTheme 时,也会在 localStoreage 里存储
  • forcedTheme: 上面的问题正需要这个属性了,这个属性是当前页面的强制主题色,使用方式如下
    function Providers({ children }) {
        const isForceDark = '根据路径或者其他方式判断'
        return(
         <ThemeProvider
          attribute="class"
          themes={['light', 'dark']}
          forcedTheme={isForceDark ? 'dark' : null}
         >
           {children}
         </ThemeProvider>
        )
    }
    
    
  • enableSystem = true: 是否跟随系统设置切换亮暗色

Hydration Error

在过去的一个月里,我的Next应用遇到这样一个问题:本地开发没有报错,但部署上线后却报了 10 个错误,打开一看全是hydration相关的

我们知道,hydration报错本质上就是ssrcsr不匹配,但线上报错本地不报错,着实让我摸不着头脑。这时组长给我明确了一个方向就是:ssr 的数据异常

还别说,打开network查看服务端渲染返回的HTML,还真找到了我的代码纰漏。不过修复后却依然存在这个报错,这是为什么呢?

原来就是这个next-themes库导致的问题,因为当 ssr 的时候其实拿不到theme此时为空,所以需要在 ssr 的时候返回 null,等到 csr 的时候再返回真正的UI

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

const ThemeSwitch = () => {
  const [mounted, setMounted] = useState(false)
  const { theme, setTheme } = useTheme()

  // useEffect only runs on the client, so now we can safely show the UI
  useEffect(() => {
    setMounted(true)
  }, [])

  if (!mounted) {
    return null
  }

  return (
    <select value={theme} onChange={e => setTheme(e.target.value)}>
      <option value="system">System</option>
      <option value="dark">Dark</option>
      <option value="light">Light</option>
    </select>
  )
}

export default ThemeSwitch

原文链接:https://juejin.cn/post/7356080597600059430 作者:祇祇

(0)
上一篇 2024年4月11日 上午10:05
下一篇 2024年4月11日 上午10:16

相关推荐

发表回复

登录后才能评论