从useEffect动态呈现样式表?

Posted

技术标签:

【中文标题】从useEffect动态呈现样式表?【英文标题】:Dynamically render style sheet from useEffect? 【发布时间】:2021-03-07 20:30:16 【问题描述】:

我正在尝试使用一个按钮(传递给另一个组件)来切换暗模式。最初,我加载了一个轻主题样式表。然后,切换主题按钮应移除页面上最新加载的样式表,并将状态设置为与当前状态相反的状态。

我的理解是,useEffect 会“看到”状态变化并开始执行。这就是我们加载新样式表的地方。该应用程序正确加载了初始样式表,并且切换按钮可以正常工作一次,但在那之后就不行了。第一次更改后,它会删除所有样式。使用 react 开发工具,看起来状态正在正确更新。我认为我对 useEffect 和/或 useState 的理解是错误的。任何指针或帮助表示赞赏!

import  useState, useEffect  from "react";
import Navi from "./Components/Layout/Navi";
import Price from "./Components/Price";
import  BrowserRouter as Router, Route  from "react-router-dom";
import  Container  from "react-bootstrap";
import About from "./Components/About/About";
require("bootswatch/dist/cosmo/bootstrap.min.css");

function App() 
    const [theme, setTheme] = useState("light");
    const toggleTheme = () => 
        Array.from(document.getElementsByTagName("style")).slice(-1)[0].remove();
        setTheme(theme === "dark" ? "light" : "dark");
    ;
    useEffect(() => 
        console.log("fire");
        console.log(theme);
        if (theme === "light") 
            require("bootswatch/dist/cosmo/bootstrap.min.css");
         else if (theme === "dark") 
            require("bootswatch/dist/slate/bootstrap.min.css");
         else 
            console.log("Theme state not correctly set");
        
    );
    return (
        <Router path="/">
            <>
                <div className="App">
                    <Navi toggleTheme=() => toggleTheme() />
                    <Container>
                        <Route path="/" exact component=Price />
                        <Route path="/about" exact component=About />
                    </Container>
                </div>
            </>
        </Router>
    );


export default App;

【问题讨论】:

【参考方案1】:

您对useEffect 的理解似乎是准确的。当您期望它们执行时,它们正在执行。 import 没有像您预期的那样发生。

当你 require 一个 css 文件时,它会在你的 dom 中添加一个 html 标签。

<link rel="stylesheet" type="text/css" href="/styles/overrides.css">

requireing 另一个 CSS 文件不会撤消已发生的 require - 它仍在添加到您的浏览器样式表中。你可以把它想象成导入一个 JS 文件而不是渲染一个组件。

选项 1:添加您自己的 &lt;style&gt; 标签

您最直接的选择是将这两个文件作为字符串导入,然后在&lt;style&gt; 标记中应用所需的样式。这是“快速和简单”的选项,但包含一些折衷(例如,无论如何您都在包中的样式中导入两个主题)。

import React,  useState  from "react";
// using inline webpack loaders for convenience of the example in Code Sandbox
import mainCSS from "!!raw-loader!./styles.css";
import altCSS from "!!raw-loader!./altstyles.css";
// Using helmet to bring the styles into the head (not necessary)
import  Helmet  from "react-helmet";

console.log(mainCSS);
export default function App() 
  const [showAltStyles, setShowAltStyles] = useState(false);
  return (
    <div className="App">
      <Helmet>
        <style>showAltStyles ? altCSS : mainCSS</style>
      </Helmet>
      <h1>Hello CodeSandbox</h1>
      <h2>Start editing to see some magic happen!</h2>
      <button onClick=() => setShowAltStyles(!showAltStyles)>
        Toggle Alt Styles
      </button>
    </div>
  );


带有实现的example Code Sandbox。

选项 2:CSS 变量

更强大的选择是使用CSS variables 并使用设置属性的css 类覆盖它们。这需要更多的工作来调整您的 bootswatch 主题。

:root 
  --primary: #FFFFFF;


.dark-theme 
  --primary: #000000;

此选项通过交换为每种样式定义的变量来避免重复样式。如果您选择此选项,您应该考虑browser support,以及您是否可以在不受支持的浏览器上设置主题。

【讨论】:

以上是关于从useEffect动态呈现样式表?的主要内容,如果未能解决你的问题,请参考以下文章

React 中带有 useEffect 的异步上下文

如何取消firebase的useEffect订阅

React:使用 Refs 修复缺少的依赖警告 useEffect

React:如何使用功能性 usestate/useEffect 复制特定的组件类 setstate 回调示例?

如何在样式组件中动态呈现伪内容之前

导航栏不会根据路由条件在 app.js 中呈现。路由更改时不会触发 Useeffect