You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让Next.js页面感知暗黑模式切换并适配Cytoscape样式?

解决Next.js + Tailwind + Cytoscape暗黑模式适配问题

问题背景

我正在开发第一个Next.js+Tailwind简易应用,包含固定头部和侧边栏,侧边栏内置暗黑/亮色模式切换按钮,用next-themes结合Tailwind实现模式切换的流程顺畅。但其中一个主内容页的有向图用Cytoscape实现,它不支持Tailwind类样式,样式表需手动定义,希望节点和边的颜色能随暗黑模式切换同步变化。

Cytoscape样式表示例:

const stylesheet = [
    {
    selector: 'node',
    style: {
        'backgroundColor': "#33FF66", // 需要随暗黑模式变色
    },
    {
    selector: "edge",
    style: {
        'curve-style': 'bezier',
        'line-color': '#333333', // 需要随暗黑模式变色
    },
    },
];

最初的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>
  );
}

最初的困惑:计划创建stylesheetLight和stylesheetDark两套样式表实现切换,但不知道如何在图表页面监听侧边栏切换按钮的事件。


更新1:将ThemeProvider移至RootLayout

按照建议把ThemeProvider从侧边栏组件移到RootLayout中,让所有子组件都能通过useTheme钩子获取主题状态:

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>
  );
}

此时图表页面可以通过useTheme获取主题变化,但直接在组件顶层根据theme设置stylesheet会触发Too Many Re-renders错误:

export default function GraphPage() {
  const { theme, setTheme } = useTheme()
  const [topicGraph, setTopicGraph] = useState<any[] | null>(null);
  const [stylesheet, setStylesheet] = useState<any | null>(null)

  console.log("Theme:", theme) // 切换模式时正常打印
  
  // 错误写法:会导致无限重渲染
  // if (theme === "light") {
  //   setStylesheet(stylesheetLight);
  // } else {
  //   setStylesheet(stylesheetDark);
  // }

  useEffect(() => {
    getGraph().then((jsonResponse) => {
      // 这里设置样式表正常工作
      if (theme === "light") {
        setStylesheet(stylesheetLight);
      } else {
        setStylesheet(stylesheetDark);
      }

      // 调用API获取图表数据...
      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>
  )
}

更新2:解决无限重渲染问题

将setStylesheet逻辑移入依赖theme的useEffect块中,确保只有当theme变化时才更新样式表,彻底避免无限重渲染:

核心修改后的代码片段:

useEffect(() => {
  // 根据当前主题切换样式表
  if (theme === "light") {
    setStylesheet(stylesheetLight);
  } else {
    setStylesheet(stylesheetDark);
  }

  // 若主题变化时无需重新获取数据,可移除以下API调用
  getGraph().then((jsonResponse) => {
    setTopicGraph(graph);
  });
}, [theme]); // 依赖项仅包含theme,确保仅在theme变化时执行

这样既实现了Cytoscape图表样式随暗黑模式同步切换,又解决了重渲染问题。


内容的提问来源于stack exchange,提问作者Christian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 14:26:22