haixunMaster/components/theme-provider.tsx

61 lines
1.4 KiB
TypeScript
Raw Permalink Normal View History

2026-06-21 12:50:31 +00:00
"use client";
import {
createContext,
useCallback,
useContext,
useEffect,
useState,
type ReactNode,
} from "react";
import {
DEFAULT_THEME,
type ThemeMode,
persistTheme,
readStoredTheme,
} from "@/lib/theme";
interface ThemeContextValue {
theme: ThemeMode;
setTheme: (mode: ThemeMode) => void;
toggleTheme: () => void;
}
const ThemeContext = createContext<ThemeContextValue | null>(null);
export function ThemeProvider({ children }: { children: ReactNode }) {
// 首屏必須與 SSR 一致DEFAULT_THEMElocalStorage 在 useEffect 讀取,避免 hydration mismatch。
const [theme, setThemeState] = useState<ThemeMode>(DEFAULT_THEME);
useEffect(() => {
const stored = readStoredTheme();
if (stored) setThemeState(stored);
}, []);
const setTheme = useCallback((mode: ThemeMode) => {
setThemeState(mode);
persistTheme(mode);
}, []);
const toggleTheme = useCallback(() => {
setThemeState((prev) => {
const next = prev === "dark" ? "light" : "dark";
persistTheme(next);
return next;
});
}, []);
return (
<ThemeContext.Provider value={{ theme, setTheme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
}
export function useTheme() {
const ctx = useContext(ThemeContext);
if (!ctx) {
throw new Error("useTheme must be used within ThemeProvider");
}
return ctx;
}