Я разрабатываю свое первое простое приложение Next.js+Tailwind. Он имеет фиксированный заголовок и боковую панель. Боковая панель содержит кнопку для переключения между темным и светлым режимом. Я использую next-theme, и с Tailwind до сих пор это было довольно просто.
Теперь одна из главных страниц контента содержит ориентированный граф. Сейчас я использую для этого Cytoscape, это в целом отличная библиотека. Моя главная проблема сейчас заключается в том, что Cytoscape не поддерживает стиль, подобный Tailwind. Создание графика Cytoscape требует ввода следующим образом:
const stylesheet = [
{
selector: 'node',
style: {
'backgroundColor': "#33FF66", // this color should change in dark mode
},
{
selector: "edge",
style: {
'curve-style': 'bezier',
'line-color': '#333333', // this color should change in dark mode
},
},
];
Другими словами, я бы хотел, чтобы цвета моего графика менялись при переключении темного режима. В настоящее время я рассматриваю необходимость создания таблиц стилей (например, stylesheetLight и stylesheetDark), а затем переключения между обоими листами при нажатии кнопки переключения. Однако я понятия не имею, как прослушивать кнопку переключения, расположенную на боковой панели страницы, содержащей график. Мой RootLayout выглядит следующим образом:
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<html lang = "en">
<body className = "${inter.className} bg-slate-50 dark:bg-slate-800">
<div className = "grid min-h-screen grid-rows-header">
<div>
<Header onMenuButtonClick = {() => setSidebarOpen((prev) => !prev)} />
</div>
<div className = "flex min-h-screen">
<div className = "">
<Sidebar open = {sidebarOpen} setOpen = {setSidebarOpen} />
</div>
<div className = "mt-[50px] flex-1">
<main>{children}</main>
</div>
</div>
</div>
</body>
</html>
);
}
Как я могу это сделать? Есть ли у меня доступ к кнопке переключения на странице с графиком для добавления прослушивателя событий?
Или это вообще правильный подход?
ОБНОВЛЕНИЕ 1: Следуя ответам @ayex @Ganesh, я изменил свой RootLayout следующим образом.
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<html lang = "en">
<body className = "${inter.className} bg-slate-50 dark:bg-slate-800">
<ThemeProvider attribute = "class" defaultTheme = "system" enableSystem disableTransitionOnChange>
<div className = "grid min-h-screen grid-rows-header">
<div>
<Header onMenuButtonClick = {() => setSidebarOpen((prev) => !prev)} />
</div>
<div className = "flex min-h-screen">
<div className = "">
<Sidebar open = {sidebarOpen} setOpen = {setSidebarOpen} />
</div>
<div className = "mt-[50px] flex-1">
<main>{children}</main>
</div>
</div>
</div>
</ThemeProvider>
</body>
</html>
);
}
Изменение заключается в том, что я переместил ThemeProvider из компонента боковой панели в RootLayout. Кажется, это помогает, поскольку теперь я могу «видеть» изменение темы на моей странице графика, которая по своей сути выглядит следующим образом:
export default function GraphPage() {
const { theme, setTheme } = useTheme()
const [topicGraph, setTopicGraph] = useState<any[] | null>(null);
const [stylesheet, setStylesheet] = useState<any | null>(null)
// GOOD: Prints every time I toggle the dark mode
console.info("Theme:", theme)
// BAD: this causes "Too Many Re-renders" error
//if (theme === "light") {
// setStylesheet(stylesheetLight);
//} else {
// setStylesheet(stylesheetDark);
//}
useEffect(() => {
getGraph().then((jsonResponse) => {
// THIS WORKS!
if (theme === "light") {
setStylesheet(stylesheetLight);
} else {
setStylesheet(stylesheetDark);
}
//...Call API to fetch graph data...
setTopicGraph(graph);
});
}, [theme]);
return (
<div>
<CytoscapeComponent
elements = {topicGraph}
style = {{ width: 'w-full', height: 'calc(100vh - 50px)' }}
stylesheet = {stylesheet}
layout = {defaultLayout}
wheelSensitivity = {0.15}
cy = {(cy) => {
setListeners(cy);
}}
/>
</div>
)
}
Как отмечено в приведенном выше фрагменте кода, оператор console.info теперь всегда печатает текущий режим (темный или светлый). Однако, если я теперь использую там setStylesheet, я получаю ошибку «слишком много повторных рендерингов». Как мне этого избежать?
ОБНОВЛЕНИЕ 2: я заставил его работать, переместив setStylesheet в блок useEffect — я редактирую приведенный выше фрагмент кода. Важно, что theme — это зависимость.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


если вы используете темы next, то это просто: крючок useTheme дает вам состояние темы, и в зависимости от этого вы можете управлять стилем, применяемым к графику, как вы упомянули. пока, кстати, я не думаю, что вы еще использовали темы next, потому что, насколько я вижу, в вашем корневом макете они не настроены. вот как вы можете его использовать, настраивайте по своему усмотрению.
РутЛайаут.tsx
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<html lang = "en">
<body className = "${inter.className} bg-slate-50 dark:bg-slate-800">
<ThemeContextProvider>
<div className = "grid min-h-screen grid-rows-header">
<div>
<Header onMenuButtonClick = {() => setSidebarOpen((prev) => !prev)} />
</div>
<div className = "flex min-h-screen">
<div className = "">
<Sidebar open = {sidebarOpen} setOpen = {setSidebarOpen} />
</div>
<div className = "mt-[50px] flex-1">
<main>{children}</main>
</div>
</div>
</div>
</ThemeContextProvider>
</body>
</html>
);
}
контекст/тема-context.tsx
"use client";
import { ThemeProvider } from "next-themes";
type ThemeContextProviderProps = {
children: React.ReactNode;
};
export default function ThemeContextProvider({
children,
}: ThemeContextProviderProps) {
return <ThemeProvider attribute = "class">{children}</ThemeProvider>;
}
Боковая панель.tsx
"use client";
import { useTheme } from "next-themes";
import React from "react";
import { BsMoon, BsSun } from "react-icons/bs";
export default function Sidebar() {
const { theme, setTheme } = useTheme();
const toggleTheme = () => {
if (theme === "light") {
setTheme("dark");
} else {
setTheme("light");
}
};
return (
<button
className = "fixed bottom-5 right-5 bg-white w-[3rem] h-[3rem]
bg-opacity-80 backdrop-blur-lg border border-white
border-opacity-40 shadow-2xl rounded-full flex
justify-center items-center hover:scale-105 active:scale-105
hover:backdrop-blur-none hover:bg-opacity-100 transition-all dark:bg-gray-950/80
dark:backdrop-blur-lg dark:hover:bg-gray-950 dark:border-dark/40"
onClick = {toggleTheme}
>
{theme === "light" ? <BsSun /> : <BsMoon />}
</button>
);
}
Итак, на вашем Sidebar.tsx вы можете сделать что-то вроде theme==='light'?stylesheetLight:stylesheetDark
подробнее об этом можно прочитать здесь
и если вы используете shadcn-ui: библиотеку компонентов на основе Tailwindcss, у них есть собственная документация о том, как установить темный режим и использовать его с темами next здесь
приятно знать, что ты понял это @Christian.
@Кристиан, выбрав ответ в качестве ответа, будет воодушевлен, если это вам немного поможет, спасибо.
context/theme-context.tsx
return <ThemeProvider attribute = "class">{children}</ThemeProvider>;
replace above line by below line
return <ThemeProvider attribute = "class" defaultTheme = "dark">{children}</ThemeProvider>;
you forgot to add defaultTheme = "dark" that's why your code is not working...
дайте мне знать, если проблема не решена.
Я использую defaultTheme = "system" в своем коде. Я переместил ThemeProvider из боковой панели в RootLayout, что теперь помогает «прослушивать» любые изменения в theme на странице графика. Однако использование этого для обновления таблицы стилей графика приводит к ошибке «слишком много повторных рендерингов»; см. обновленный исходный пост.
покажите мне код, в котором вы вызываете компонент Graph
Я заставил это работать; смотрите мое последнее обновление. Еще раз спасибо!
Как сейчас написано, ваш ответ неясен. Пожалуйста, отредактируйте , чтобы добавить дополнительную информацию, которая поможет другим понять, как это относится к заданному вопросу. Более подробную информацию о том, как писать хорошие ответы, вы можете найти в справочном центре.
Спасибо! Да, перемещение
ThemeProviderиз боковой панели вRootLayoutуже привело к большим изменениям. Однако я не уверен, как теперь правильно обработать это на странице графика. Я «вижу» изменениеthemeна странице графика каждый раз, когда переключаюсь с помощью кнопки на боковой панели, но моя попытка обработать приводит к ошибке «слишком много повторных рендерингов»; см. обновление в моем исходном сообщении с фрагментами кода.