Files
SaaS2/src/hooks/useTheme.tsx
2025-06-05 23:05:33 +08:00

118 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 主题管理Hook
* @author 阿瑞
* @description 主题切换和状态管理的自定义Hook支持SSR和防闪烁优化性能优化
* @version 2.1.0
* @created 2024-12-19
* @updated 优化性能,简化动画逻辑
*/
import React, { createContext, useContext, useEffect, useState, useCallback } from 'react';
import { useSettingActions, useThemeMode } from '@/store/settingStore';
import { ThemeMode } from '@/types/enum';
// 模块级注释:主题切换上下文类型定义
export interface ThemeContextType {
isDark: boolean;
toggleTheme: () => void;
currentTheme: ThemeMode;
mounted: boolean; // 关键代码行注释添加mounted状态避免SSR不匹配
isTransitioning: boolean; // 关键代码行注释:添加过渡状态追踪
}
// 模块级注释:创建主题上下文
const ThemeContext = createContext<ThemeContextType>({
isDark: false,
toggleTheme: () => {},
currentTheme: ThemeMode.Light,
mounted: false,
isTransitioning: false,
});
// 模块级注释主题切换Hook
export const useTheme = (): ThemeContextType => {
const context = useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
// 模块级注释:主题提供者组件
export function ThemeProvider({ children }: { children: React.ReactNode }): React.ReactElement {
// 关键代码行注释使用zustand store管理主题状态
const themeMode = useThemeMode();
const { toggleThemeMode } = useSettingActions();
const isDark = themeMode === ThemeMode.Dark;
// 关键代码行注释添加mounted状态确保只在客户端应用主题
const [mounted, setMounted] = useState(false);
const [isTransitioning, setIsTransitioning] = useState(false);
// 关键代码行注释:简化的主题应用函数
const applyTheme = useCallback((darkMode: boolean): void => {
if (typeof document !== 'undefined' && mounted) {
const root = document.documentElement;
const body = document.body;
// 关键代码行注释:清除所有主题相关的类名
body.classList.remove('light-theme', 'dark-theme', 'light', 'dark');
root.classList.remove('light-theme', 'dark-theme', 'light', 'dark');
// 关键代码行注释:应用新的主题类名
const themeClass = darkMode ? 'dark' : 'light';
body.classList.add(themeClass);
root.classList.add(themeClass);
// 关键代码行注释设置data-theme属性
body.setAttribute('data-theme', themeClass);
root.setAttribute('data-theme', themeClass);
}
}, [mounted]);
// 关键代码行注释:优化的主题切换函数
const enhancedToggleTheme = useCallback(() => {
if (!isTransitioning) {
setIsTransitioning(true);
// 关键代码行注释:触发主题切换
toggleThemeMode();
// 关键代码行注释:配合分层过渡策略的完成处理
setTimeout(() => {
setIsTransitioning(false);
}, 400); // 配合CSS主过渡时间
// 关键代码行注释:添加轻量触觉反馈(如果支持)
if ('vibrate' in navigator) {
navigator.vibrate(30);
}
}
}, [toggleThemeMode, isTransitioning]);
// 关键代码行注释组件挂载时设置mounted状态
useEffect(() => {
setMounted(true);
}, []);
// 关键代码行注释:只有在客户端挂载后才应用主题
useEffect(() => {
if (mounted) {
applyTheme(isDark);
}
}, [isDark, mounted, applyTheme]);
return (
<ThemeContext.Provider
value={{
isDark,
toggleTheme: enhancedToggleTheme,
currentTheme: themeMode,
mounted,
isTransitioning
}}
>
{children}
</ThemeContext.Provider>
);
}