Dynamically load a stylesheet with React

前端 未结 5 1633
夕颜
夕颜 2020-11-29 02:16

I\'m building a CMS system for managing marketing landing pages. On the \"Edit Landing Page\" view, I want to be able to load the associated stylesheet for whichever landing

相关标签:
5条回答
  • 2020-11-29 02:27

    This is how I add style dynamically:

    import React, { Component } from "react";
    
    class MyComponent extends Component {
        componentDidMount() {
            const cssUrl = "/public/assets/css/style.css";
            this.addStyle(cssUrl);
        }
    
        addStyle = url => {
            const style = document.createElement("link");
            style.href = url;
            style.rel = "stylesheet";
            style.async = true;
    
            document.head.appendChild(style);
        };
    
        render() {
            return <div> textInComponent </div>;
        }
    }
    
    export default MyComponent;
    
    0 讨论(0)
  • 2020-11-29 02:33

    I think that Burakhan answer is correct but it is weird to load <Link href = "" /> inside the body tag. That's why I think it should be modified to the following [ I use React hooks]:

    import * as React from 'react';
    export default MainPage = (props) => {
      const [ stylePath, setStylePath ] = useState("style1.css");
        
      const handleButtonClick = () => {
        setStylePath({stylePath: 'style2.css'});
      }
    
      useEffect(() => {
        var head = document.head;
        var link = document.createElement("link");
    
        link.type = "text/css";
        link.rel = "stylesheet";
        link.href = stylePath;
    
        head.appendChild(link);
    
        return () => { head.removeChild(link); }
    
      }, [stylePath]);
    
      return (
        <div>
          <button type="button" onClick={handleButtonClick}>
            Click to update stylesheet
          </button>
        </div>
      );
    };
    
    0 讨论(0)
  • 2020-11-29 02:36

    Just update stylesheet's path that you want to be dynamically loaded by using react's state.

    import * as React from 'react';
    
    export default class MainPage extends React.Component{
        constructor(props){
            super(props);
            this.state = {stylePath: 'style1.css'};
        }
    
        handleButtonClick(){
            this.setState({stylePath: 'style2.css'});
        }
    
        render(){
            return (
                <div>
                    <link rel="stylesheet" type="text/css" href={this.state.stylePath} />
                    <button type="button" onClick={this.handleButtonClick.bind(this)}>Click to update stylesheet</button>
                </div>
            )
        }
    };
    

    Also, I have implemented it as react component. You can install via npm install react-dynamic-style-loader.
    Check my github repository to examine:
    https://github.com/burakhanalkan/react-dynamic-style-loader

    0 讨论(0)
  • 2020-11-29 02:41

    Instead of creating elements for stylesheet, you can also try importing your css based on some condition. ECMAScript provides a proposal that enables dynamic module imports, that works as follows:

    if (condition) {
      import('your css path here').then((condition) => {});
    }
    
    0 讨论(0)
  • 2020-11-29 02:48

    This is prime mixin teritority. First we'll define a helper to manage style sheets.

    We need a function that loads a style sheet, and returns a promise for its success. Style sheets are actually pretty insane to detect load on...

    function loadStyleSheet(url){
      var sheet = document.createElement('link');
      sheet.rel = 'stylesheet';
      sheet.href = url;
      sheet.type = 'text/css';
      document.head.appendChild(sheet);
      var _timer;
    
      // TODO: handle failure
      return new Promise(function(resolve){
        sheet.onload = resolve;
        sheet.addEventListener('load', resolve);
        sheet.onreadystatechange = function(){
          if (sheet.readyState === 'loaded' || sheet.readyState === 'complete') {
            resolve();
          }
        };
    
        _timer = setInterval(function(){
          try {
            for (var i=0; i<document.styleSheets.length; i++) {
              if (document.styleSheets[i].href === sheet.href) resolve();
            } catch(e) { /* the stylesheet wasn't loaded */ }
          }
        }, 250);
      })
      .then(function(){ clearInterval(_timer); return link; });
    }
    

    Well $#!@... I was expecting to just stick an onload on it, but nope. This is untested, so please update it if there are any bugs – it's compiled from several blog articles.

    The rest is fairly straight forward:

    • allow loading a stylesheet
    • update state when it's available (to prevent FOUC)
    • unload any loaded stylesheets when the component unmounts
    • handle all the async goodness
    var mixin = {
      componentWillMount: function(){
        this._stylesheetPromises = [];
      },
      loadStyleSheet: function(name, url){
        this._stylesheetPromises.push(loadStyleSheet(url))
        .then(function(link){
          var update = {};
          update[name] = true;
          this.setState(update);
        }.bind(this));
      },
      componentWillUnmount: function(){
        this._stylesheetPromises.forEach(function(p){
          // we use the promises because unmount before the download finishes is possible
          p.then(function(link){
            // guard against it being otherwise removed
            if (link.parentNode) link.parentNode.removeChild(link);
          });
        });
      }
    };
    

    Again, untested, please update this if there are any issues.

    Now we have the component.

    React.createClass({
      getInitialState: function(){
        return {foo: false};
      },
      componentDidMount: function(){
        this.loadStyleSheet('foo', '/css/views/foo.css');
      },
      render: function(){
        if (!this.state.foo) {
          return <div />
        }
    
        // return conent that depends on styles
      }
    });
    

    The only remaining todo is checking if the style sheet already exists before trying to load it. Hopefully this at least gets you on the right path.

    0 讨论(0)
提交回复
热议问题