useDarkMode

具有自動數據持久性的深色模式。

useDarkMode 提供了簡單的明暗模式切換功能,並自動持久化到 localStorage。它回傳一個包含當前主題狀態和切換函式的元組。此 hook 將 CSS 類別名稱(可透過 classNameDarkclassNameLight 設定)套用到 <html> 元素,並在沒有已儲存值時透過 prefers-color-scheme 媒體查詢與使用者的系統偏好同步。它預設尊重系統偏好,但允許使用者覆寫。

使用場景

  • 為應用程式新增具有自動持久化的深色模式切換
  • 建構一個以使用者系統色彩方案偏好為預設值的主題切換器
  • 將明暗 CSS 類別套用到根元素以實現 CSS 驅動的主題設定

注意事項

  • SSR 安全:在伺服器端渲染時,深色模式狀態回傳 false。伺服器上不會存取 DOM 或 localStorage。請參閱下方的 Tips 章節以防止未樣式化內容的閃爍。
  • 持久化:將偏好儲存在 localStorage 中,鍵名為 "reactuses-color-scheme"。已儲存的值會在 v6.1.0+ 中自動從舊的布林格式遷移到新的字串格式。
  • 相關 hooks:如需超越明暗的多主題支援,請參閱 useColorMode。如需在不持久化的情況下偵測系統偏好,請參閱 usePreferredDarkusePreferredColorScheme

⚠️ v6.1.0 破壞性變更

存儲格式已從布爾值更改為字符串值。 如果你正在从旧版本升级:

  • 旧格式: true/false 布尔值
  • 新格式: "dark"/"light" 字符串值
  • 自动迁移: 现有数据将自动迁移
  • SSR 脚本: 需要更新以處理字符串比较
迁移指南

如果你有自定义的 SSR 脚本,请按照下面示例中的方式进行更新。库会自动處理数据迁移,但你的自定义脚本需要在过渡期间處理新旧两种格式。

Tips

点击展开 对于服务端渲染的应用程序而言,由于无法在服务端獲取到用户的颜色偏好。所以第一次渲染的时候可能会出现闪烁。想要避免此问题,你可以参考一下步骤。

  1. 在你的內容渲染前增加一个脚本。
<script
  dangerouslySetInnerHTML={{
    // 增加一个自执行的函數
    __html: `
          (function () {
            function setDark(dark) {
              dark &&  document.documentElement.classList.add('dark');
            }
            let store = localStorage.getItem('reactuses-color-scheme');
            let dark;
            if(store === null){
              const darkQuery = window.matchMedia('(prefers-color-scheme: dark)');
              dark = darkQuery.matches;
            }else {
              // 處理向后兼容性:将旧的布尔值转换为字符串
              if (store === 'true' || store === 'false') {
                const boolValue = store === 'true';
                const stringValue = boolValue ? 'dark' : 'light';
                localStorage.setItem('reactuses-color-scheme', stringValue);
                dark = boolValue;
              } else {
                // 直接處理字符串值 ('dark' 或 'light')
                dark = store === 'dark';
              }
            }
            setDark(dark)
          })();
      `,
  }}
></script>
  1. 为了方便统一管理主题颜色,我们推荐使用 context 来存储它们。
import { useDarkMode } from "@reactuses/core";
import React, { createContext, useContext } from "react";

type ThemeContext = { theme: boolean; toggleTheme: () => void };

const ThemeContext = createContext<ThemeContext | undefined>(undefined);

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const [dark, toggle] = useDarkMode({
    classNameDark: "dark",
    classNameLight: "light",
    defaultValue: false,
  });

  return (
    <ThemeContext.Provider value={{ theme: !!dark, toggleTheme: toggle }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (context === undefined) {
    throw new Error("useTheme must be used within a ThemeProvider");
  }
  return context;
}

Usage

Live Editor

function Demo() {
  const [theme, toggleDark] = useDarkMode({
    classNameDark: "dark",
    classNameLight: "light",
    defaultValue: false,
  });

  return (
    <div>
      <div>theme: {theme ? "dark" : "light"}</div>
      <br />
      <div>
        <button onClick={toggleDark}>toggleDark</button>
      </div>
    </div>
  );
};
Result

API

UseDarkOptions

參數名描述類型預設值
selector适用于目标元素的 CSS 选择器string'html'
attribute应用到目标元素的 html 属性string'class'
defaultValue默认值booleanfalse
storageKey将数据持久保存到 localStorage/sessionStorage 的键值string'reactuses-color-scheme'
storage存储对象,可以是localStorage或sessionStorage() => StoragelocalStorage
classNameDark应用到目标元素上黑色类名称string (必填)-
classNameLight应用到目标元素上的亮色类名称string (必填)-

useDarkMode

Returns

readonly [boolean | null, () => void, React.Dispatch<React.SetStateAction<boolean | null>>]: 包含以下元素的元組:

  • 黑暗狀態的當前值。
  • 切換黑暗狀態的功能。
  • 更新黑暗狀態的功能。

Arguments

參數名描述類型預設值
options-UseDarkOptions (必填)-