Multi-page public site (Next.js), Node/Express backend with Postgres, and Vite admin/account app, restructured from a single-page layout into dedicated service, promotions, cases, about, FAQ, and blog pages for SEO. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013otXCiTZsxdZ4SJKZ9wUky
36 lines
1.6 KiB
React
36 lines
1.6 KiB
React
'use client';
|
|
|
|
import { createContext, useContext, useEffect, useState } from 'react';
|
|
|
|
const ThemeContext = createContext({ dark: false, toggle: () => {} });
|
|
|
|
export const useTheme = () => useContext(ThemeContext);
|
|
|
|
export function ThemeProvider({ children }) {
|
|
// Инлайн-скрипт в layout.jsx уже выставляет класс "dark" на <html> ДО гидратации
|
|
// (чтобы не было мигания темы). Читаем этот класс напрямую при монтировании.
|
|
const [dark, setDark] = useState(() => {
|
|
if (typeof document === 'undefined') return false;
|
|
return document.documentElement.classList.contains('dark');
|
|
});
|
|
|
|
// Побочные эффекты (DOM-класс, localStorage) вынесены в отдельный useEffect,
|
|
// а не в апдейтер состояния - React может вызвать функцию апдейтера больше
|
|
// одного раза за один рендер, и тогда classList.toggle сработал бы дважды,
|
|
// фактически отменяя переключение (баг "не срабатывает" / "снова светлая").
|
|
useEffect(() => {
|
|
document.documentElement.classList.toggle('dark', dark);
|
|
try {
|
|
localStorage.setItem('ts-theme', dark ? 'dark' : 'light');
|
|
} catch (e) {
|
|
// localStorage unavailable - theme just won't persist
|
|
}
|
|
}, [dark]);
|
|
|
|
return (
|
|
<ThemeContext.Provider value={{ dark, toggle: () => setDark((d) => !d) }}>
|
|
{children}
|
|
</ThemeContext.Provider>
|
|
);
|
|
}
|